This is an automated email from the ASF dual-hosted git repository.
yjhjstz 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 5306ce52cff Pax: fix delete bloom crash (#1755)
5306ce52cff is described below
commit 5306ce52cff27bf682b15daf7f279f3cbdc8a143
Author: Jianghua.yjh <[email protected]>
AuthorDate: Thu Sep 10 21:40:54 2026 -0700
Pax: fix delete bloom crash (#1755)
* PAX: include bloomfilter columns in DELETE stats projection
DeleteWithVisibilityMap projected only minmax_columns into the reader,
then asked UpdateStatsInAuxTable to refresh bloomfilter stats too. When
a bloomfilter column was not also a minmax column, the reader did not
materialize it, and MicroPartitionStats::AddRow dereferenced an
uninitialized slot value, crashing the segment with SIGSEGV.
Project the union of minmax and bloomfilter column indexes, while
keeping the original lists passed to UpdateStatsInAuxTable so per-stat
semantics are unchanged. Hoist the GetBloomFilterColumnIndexes() call
out of the per-block loop.
Fixes apache/cloudberry#1749
---
.../pax_storage/expected/delete_bloom_stats.out | 60 ++++++++++++++++++++++
contrib/pax_storage/pax_schedule | 1 +
contrib/pax_storage/sql/delete_bloom_stats.sql | 48 +++++++++++++++++
contrib/pax_storage/src/cpp/storage/pax.cc | 16 +++++-
4 files changed, 123 insertions(+), 2 deletions(-)
diff --git a/contrib/pax_storage/expected/delete_bloom_stats.out
b/contrib/pax_storage/expected/delete_bloom_stats.out
new file mode 100644
index 00000000000..65d4d12986d
--- /dev/null
+++ b/contrib/pax_storage/expected/delete_bloom_stats.out
@@ -0,0 +1,60 @@
+-- Regression test for issue #1749:
+-- PAX DELETE crashes with SIGSEGV when bloomfilter_columns are not a
+-- subset of minmax_columns. The stats refresh inside
+-- DeleteWithVisibilityMap must project every column it reads.
+-- Case 1: bloomfilter column (payload) is NOT in minmax_columns.
+-- Pre-fix: segment crashed on DELETE.
+drop table if exists pax_delete_bloom_crash;
+NOTICE: table "pax_delete_bloom_crash" does not exist, skipping
+create table pax_delete_bloom_crash (id int, k int, payload text)
+using pax
+with (minmax_columns = 'id', bloomfilter_columns = 'payload');
+NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'id'
as the Apache Cloudberry data distribution key for this table.
+HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make
sure column(s) chosen are the optimal data distribution key to minimize skew.
+insert into pax_delete_bloom_crash
+select i, i % 10, 'payload-' || i::text
+from generate_series(1, 10000) as i;
+delete from pax_delete_bloom_crash where id between 1 and 100;
+select count(*) from pax_delete_bloom_crash;
+ count
+-------
+ 9900
+(1 row)
+
+drop table pax_delete_bloom_crash;
+-- Case 2: bloomfilter only, no minmax columns.
+drop table if exists pax_delete_bf_only;
+NOTICE: table "pax_delete_bf_only" does not exist, skipping
+create table pax_delete_bf_only (id int, payload text)
+using pax
+with (bloomfilter_columns = 'payload');
+NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'id'
as the Apache Cloudberry data distribution key for this table.
+HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make
sure column(s) chosen are the optimal data distribution key to minimize skew.
+insert into pax_delete_bf_only
+select i, 'payload-' || i::text from generate_series(1, 5000) as i;
+delete from pax_delete_bf_only where id between 1 and 50;
+select count(*) from pax_delete_bf_only;
+ count
+-------
+ 4950
+(1 row)
+
+drop table pax_delete_bf_only;
+-- Case 3: minmax and bloomfilter columns overlap but neither is a subset.
+drop table if exists pax_delete_mm_bf_mixed;
+NOTICE: table "pax_delete_mm_bf_mixed" does not exist, skipping
+create table pax_delete_mm_bf_mixed (id int, k int, payload text)
+using pax
+with (minmax_columns = 'id,payload', bloomfilter_columns = 'k,payload');
+NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'id'
as the Apache Cloudberry data distribution key for this table.
+HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make
sure column(s) chosen are the optimal data distribution key to minimize skew.
+insert into pax_delete_mm_bf_mixed
+select i, i % 7, 'p-' || i::text from generate_series(1, 5000) as i;
+delete from pax_delete_mm_bf_mixed where id between 1 and 50;
+select count(*) from pax_delete_mm_bf_mixed;
+ count
+-------
+ 4950
+(1 row)
+
+drop table pax_delete_mm_bf_mixed;
diff --git a/contrib/pax_storage/pax_schedule b/contrib/pax_storage/pax_schedule
index 5f7ef41d2d8..c29982d8f2e 100644
--- a/contrib/pax_storage/pax_schedule
+++ b/contrib/pax_storage/pax_schedule
@@ -6,6 +6,7 @@ test: alter_distributed
test: toast toast_failed detoast ddl numeric types filter
test: update
test: statistics_bloom_filter
+test: delete_bloom_stats
test: filter_tree filter_tree_arithmetic
test: filter_tree_root_quals
diff --git a/contrib/pax_storage/sql/delete_bloom_stats.sql
b/contrib/pax_storage/sql/delete_bloom_stats.sql
new file mode 100644
index 00000000000..c422d21ffa8
--- /dev/null
+++ b/contrib/pax_storage/sql/delete_bloom_stats.sql
@@ -0,0 +1,48 @@
+-- Regression test for issue #1749:
+-- PAX DELETE crashes with SIGSEGV when bloomfilter_columns are not a
+-- subset of minmax_columns. The stats refresh inside
+-- DeleteWithVisibilityMap must project every column it reads.
+
+-- Case 1: bloomfilter column (payload) is NOT in minmax_columns.
+-- Pre-fix: segment crashed on DELETE.
+drop table if exists pax_delete_bloom_crash;
+create table pax_delete_bloom_crash (id int, k int, payload text)
+using pax
+with (minmax_columns = 'id', bloomfilter_columns = 'payload');
+
+insert into pax_delete_bloom_crash
+select i, i % 10, 'payload-' || i::text
+from generate_series(1, 10000) as i;
+
+delete from pax_delete_bloom_crash where id between 1 and 100;
+select count(*) from pax_delete_bloom_crash;
+
+drop table pax_delete_bloom_crash;
+
+-- Case 2: bloomfilter only, no minmax columns.
+drop table if exists pax_delete_bf_only;
+create table pax_delete_bf_only (id int, payload text)
+using pax
+with (bloomfilter_columns = 'payload');
+
+insert into pax_delete_bf_only
+select i, 'payload-' || i::text from generate_series(1, 5000) as i;
+
+delete from pax_delete_bf_only where id between 1 and 50;
+select count(*) from pax_delete_bf_only;
+
+drop table pax_delete_bf_only;
+
+-- Case 3: minmax and bloomfilter columns overlap but neither is a subset.
+drop table if exists pax_delete_mm_bf_mixed;
+create table pax_delete_mm_bf_mixed (id int, k int, payload text)
+using pax
+with (minmax_columns = 'id,payload', bloomfilter_columns = 'k,payload');
+
+insert into pax_delete_mm_bf_mixed
+select i, i % 7, 'p-' || i::text from generate_series(1, 5000) as i;
+
+delete from pax_delete_mm_bf_mixed where id between 1 and 50;
+select count(*) from pax_delete_mm_bf_mixed;
+
+drop table pax_delete_mm_bf_mixed;
diff --git a/contrib/pax_storage/src/cpp/storage/pax.cc
b/contrib/pax_storage/src/cpp/storage/pax.cc
index 7724cfc9602..7edeba9fa2f 100644
--- a/contrib/pax_storage/src/cpp/storage/pax.cc
+++ b/contrib/pax_storage/src/cpp/storage/pax.cc
@@ -27,8 +27,11 @@
#include "storage/pax.h"
+#include <algorithm>
+#include <iterator>
#include <map>
#include <utility>
+#include <vector>
#include "access/pax_visimap.h"
#include "access/paxc_rel_options.h"
@@ -596,6 +599,8 @@ void TableDeleter::DeleteWithVisibilityMap(
return;
}
std::vector<int> min_max_col_idxs;
+ std::vector<int> bf_col_idxs;
+ std::vector<int> stats_proj_col_idxs;
auto stats_updater_projection = std::make_shared<PaxFilter>();
std::unique_ptr<Bitmap8> visi_bitmap;
@@ -604,7 +609,14 @@ void TableDeleter::DeleteWithVisibilityMap(
rel_->rd_locator, rel_->rd_backend);
min_max_col_idxs = cbdb::GetMinMaxColumnIndexes(rel_);
- stats_updater_projection->SetColumnProjection(min_max_col_idxs,
+ bf_col_idxs = cbdb::GetBloomFilterColumnIndexes(rel_);
+
+ // Projection must cover minmax ∪ bloomfilter columns; otherwise
+ // AddRow reads uninitialized slot values for bf columns (issue #1749).
+ std::set_union(min_max_col_idxs.begin(), min_max_col_idxs.end(),
+ bf_col_idxs.begin(), bf_col_idxs.end(),
+ std::back_inserter(stats_proj_col_idxs));
+ stats_updater_projection->SetColumnProjection(stats_proj_col_idxs,
rel_->rd_att->natts);
do {
auto it = iterator->Next();
@@ -677,7 +689,7 @@ void TableDeleter::DeleteWithVisibilityMap(
UpdateStatsInAuxTable(
catalog_update, micro_partition_metadata,
std::make_shared<Bitmap8>(visi_bitmap->Raw()), min_max_col_idxs,
- cbdb::GetBloomFilterColumnIndexes(rel_), stats_updater_projection);
+ bf_col_idxs, stats_updater_projection);
// write pg_pax_blocks_oid
catalog_update.UpdateVisimap(block_id, visimap_file_name);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]