This is an automated email from the ASF dual-hosted git repository.
my-ship-it pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudberry.git
The following commit(s) were added to refs/heads/main by this push:
new e97b91909c2 PAX: fix SIGSEGV in SUM stats merge during DELETE on
minmax columns
e97b91909c2 is described below
commit e97b91909c29ae8a984a81f707c4245b134b2c9f
Author: GongXun <[email protected]>
AuthorDate: Mon Jul 27 03:02:51 2026 +0800
PAX: fix SIGSEGV in SUM stats merge during DELETE on minmax columns
When a PAX table has minmax_columns that also support SUM statistics
(e.g. int, bigint, numeric), repeated DELETE operations can crash a
segment with SIGSEGV inside datumCopy() during the visibility-map
statistics refresh path.
Two bugs were identified in MicroPartitionStats::MergeRawInfo() and
MergeTo():
1. Wrong type metadata in FromValue(): The serialized SUM value was
deserialized using the column physical type (typlen/typbyval)
instead of the SUM aggregate return type (rettyplen/rettypbyval).
For example, sum(bigint) returns numeric, so the stored SUM datum
must be interpreted as numeric, not as int8. Using the wrong type
metadata produces an invalid Datum that may crash in datumCopy().
2. Swapped arguments in datumCopy(): The call passed (value, typlen,
typbyval) but the wrapper signature is datumCopy(value, typByVal,
typLen). This caused pass-by-value vs pass-by-reference confusion,
leading to memory corruption.
Add regression test (delete_sum_stats) covering DELETE + INSERT +
DELETE on int, bigint, and numeric columns with minmax_columns enabled.
Fixes #1767
---
contrib/pax_storage/expected/delete_sum_stats.out | 53 ++++++++++++++++++++++
contrib/pax_storage/pax_schedule | 1 +
contrib/pax_storage/sql/delete_sum_stats.sql | 47 +++++++++++++++++++
.../src/cpp/storage/micro_partition_stats.cc | 10 ++--
4 files changed, 106 insertions(+), 5 deletions(-)
diff --git a/contrib/pax_storage/expected/delete_sum_stats.out
b/contrib/pax_storage/expected/delete_sum_stats.out
new file mode 100644
index 00000000000..8b4b200cd26
--- /dev/null
+++ b/contrib/pax_storage/expected/delete_sum_stats.out
@@ -0,0 +1,53 @@
+-- Verify that DELETE statistics refresh uses the SUM result type when
+-- deserializing and merging stats. In particular, sum(bigint) returns numeric.
+set pax.max_tuples_per_group = 25;
+create table pax_delete_sum_stats (
+ dist_key int,
+ id bigint,
+ int_amount int,
+ bigint_amount bigint,
+ numeric_amount numeric
+)
+using pax
+with (minmax_columns = 'int_amount,bigint_amount,numeric_amount')
+distributed by (dist_key);
+insert into pax_delete_sum_stats
+select 1, i, i, i, i::numeric * 2
+from generate_series(1, 1000) i;
+select count(*) as rows,
+ sum(int_amount) as int_sum,
+ sum(bigint_amount) as bigint_sum,
+ sum(numeric_amount) as numeric_sum
+from pax_delete_sum_stats;
+ rows | int_sum | bigint_sum | numeric_sum
+------+---------+------------+-------------
+ 1000 | 500500 | 500500 | 1001000
+(1 row)
+
+delete from pax_delete_sum_stats where id between 1 and 100;
+select count(*) as rows,
+ sum(int_amount) as int_sum,
+ sum(bigint_amount) as bigint_sum,
+ sum(numeric_amount) as numeric_sum
+from pax_delete_sum_stats;
+ rows | int_sum | bigint_sum | numeric_sum
+------+---------+------------+-------------
+ 900 | 495450 | 495450 | 990900
+(1 row)
+
+insert into pax_delete_sum_stats
+select 1, i, i, i, i::numeric * 2
+from generate_series(1001, 1100) i;
+delete from pax_delete_sum_stats where id between 301 and 400;
+select count(*) as rows,
+ sum(int_amount) as int_sum,
+ sum(bigint_amount) as bigint_sum,
+ sum(numeric_amount) as numeric_sum
+from pax_delete_sum_stats;
+ rows | int_sum | bigint_sum | numeric_sum
+------+---------+------------+-------------
+ 900 | 565450 | 565450 | 1130900
+(1 row)
+
+drop table pax_delete_sum_stats;
+reset pax.max_tuples_per_group;
diff --git a/contrib/pax_storage/pax_schedule b/contrib/pax_storage/pax_schedule
index f9818f8e0b7..5f7ef41d2d8 100644
--- a/contrib/pax_storage/pax_schedule
+++ b/contrib/pax_storage/pax_schedule
@@ -10,6 +10,7 @@ test: statistics_bloom_filter
test: filter_tree filter_tree_arithmetic
test: filter_tree_root_quals
test: statistics/statistics
+test: delete_sum_stats
test: statistics/min_max_time_types statistics/min_max_text_types
statistics/min_max_other_types statistics/min_max_net_types
statistics/min_max_int_types statistics/min_max_geo_types
statistics/min_max_float_types statistics/min_max_bit_byte_types
diff --git a/contrib/pax_storage/sql/delete_sum_stats.sql
b/contrib/pax_storage/sql/delete_sum_stats.sql
new file mode 100644
index 00000000000..dce2c9eba18
--- /dev/null
+++ b/contrib/pax_storage/sql/delete_sum_stats.sql
@@ -0,0 +1,47 @@
+-- Verify that DELETE statistics refresh uses the SUM result type when
+-- deserializing and merging stats. In particular, sum(bigint) returns numeric.
+set pax.max_tuples_per_group = 25;
+
+create table pax_delete_sum_stats (
+ dist_key int,
+ id bigint,
+ int_amount int,
+ bigint_amount bigint,
+ numeric_amount numeric
+)
+using pax
+with (minmax_columns = 'int_amount,bigint_amount,numeric_amount')
+distributed by (dist_key);
+
+insert into pax_delete_sum_stats
+select 1, i, i, i, i::numeric * 2
+from generate_series(1, 1000) i;
+
+select count(*) as rows,
+ sum(int_amount) as int_sum,
+ sum(bigint_amount) as bigint_sum,
+ sum(numeric_amount) as numeric_sum
+from pax_delete_sum_stats;
+
+delete from pax_delete_sum_stats where id between 1 and 100;
+
+select count(*) as rows,
+ sum(int_amount) as int_sum,
+ sum(bigint_amount) as bigint_sum,
+ sum(numeric_amount) as numeric_sum
+from pax_delete_sum_stats;
+
+insert into pax_delete_sum_stats
+select 1, i, i, i, i::numeric * 2
+from generate_series(1001, 1100) i;
+
+delete from pax_delete_sum_stats where id between 301 and 400;
+
+select count(*) as rows,
+ sum(int_amount) as int_sum,
+ sum(bigint_amount) as bigint_sum,
+ sum(numeric_amount) as numeric_sum
+from pax_delete_sum_stats;
+
+drop table pax_delete_sum_stats;
+reset pax.max_tuples_per_group;
diff --git a/contrib/pax_storage/src/cpp/storage/micro_partition_stats.cc
b/contrib/pax_storage/src/cpp/storage/micro_partition_stats.cc
index 53342ad0e2a..d307ed49431 100644
--- a/contrib/pax_storage/src/cpp/storage/micro_partition_stats.cc
+++ b/contrib/pax_storage/src/cpp/storage/micro_partition_stats.cc
@@ -758,7 +758,7 @@ void MicroPartitionStats::MergeRawInfo(
} else if (sum_stat->status == STATUS_NEED_UPDATE) {
sum_result =
FromValue(stats_info->columnstats(column_index).datastats().sum(),
- typlen, typbyval, column_index);
+ sum_stat->rettyplen, sum_stat->rettypbyval, column_index);
Datum newval = cbdb::FunctionCall2Coll(&sum_stat->add_func, InvalidOid,
sum_stat->result, sum_result);
if (!sum_stat->rettypbyval && newval != sum_stat->result &&
@@ -766,11 +766,11 @@ void MicroPartitionStats::MergeRawInfo(
cbdb::Pfree(cbdb::DatumToPointer(sum_stat->result));
}
sum_stat->result =
- cbdb::datumCopy(newval, sum_stat->rettyplen, sum_stat->rettypbyval);
+ cbdb::datumCopy(newval, sum_stat->rettypbyval, sum_stat->rettyplen);
} else if (sum_stat->status == STATUS_MISSING_INIT_VAL) {
sum_result =
FromValue(stats_info->columnstats(column_index).datastats().sum(),
- typlen, typbyval, column_index);
+ sum_stat->rettyplen, sum_stat->rettypbyval, column_index);
sum_stat->result = cbdb::datumCopy(sum_result, sum_stat->rettypbyval,
sum_stat->rettyplen);
sum_stat->status = STATUS_NEED_UPDATE;
@@ -966,8 +966,8 @@ void MicroPartitionStats::MergeTo(MicroPartitionStats
*stats) {
left_sum_stat->result) {
cbdb::Pfree(cbdb::DatumToPointer(left_sum_stat->result));
}
- left_sum_stat->result = cbdb::datumCopy(newval, left_sum_stat->rettyplen,
- left_sum_stat->rettypbyval);
+ left_sum_stat->result = cbdb::datumCopy(
+ newval, left_sum_stat->rettypbyval, left_sum_stat->rettyplen);
} else if (left_sum_stat->status == STATUS_MISSING_INIT_VAL) {
left_sum_stat->result =
cbdb::datumCopy(right_sum_stat->result, left_sum_stat->rettypbyval,
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]