diff --git a/src/gausskernel/cbb/utils/partition/partrouting.cpp b/src/gausskernel/cbb/utils/partition/partrouting.cpp index 606598bc1..79e497b15 100644 --- a/src/gausskernel/cbb/utils/partition/partrouting.cpp +++ b/src/gausskernel/cbb/utils/partition/partrouting.cpp @@ -404,6 +404,7 @@ Oid getHashPartitionOid(PartitionMap* partMap, Const** partKeyValue, int32* part Oid result = InvalidOid; int keyNums = 0; int hit = -1; + bool hasNull = false; Assert(PointerIsValid(partMap)); Assert(PointerIsValid(partKeyValue)); @@ -417,6 +418,10 @@ Oid getHashPartitionOid(PartitionMap* partMap, Const** partKeyValue, int32* part uint32 hash_value = 0; while (i < keyNums) { if (partKeyValue[i]->constisnull) { + if (DB_IS_CMPT(A_FORMAT)) { + hasNull = true; + break; + } if (PointerIsValid(partSeq)) { *partSeq = hit; } @@ -428,16 +433,19 @@ Oid getHashPartitionOid(PartitionMap* partMap, Const** partKeyValue, int32* part i++; } - hit = hash_value % (uint32)(hashPartMap->hashElementsNum); - hit = hashPartMap->hashElementsNum - hit - 1; + if (hasNull && DB_IS_CMPT(A_FORMAT)) { + /* If null exists for hash partition, force route to first partition */ + hit = 0; + } else { + hit = hash_value % (uint32)(hashPartMap->hashElementsNum); + hit = hashPartMap->hashElementsNum - hit - 1; + } if (PointerIsValid(partSeq)) { *partSeq = hit; } - if (hit >= 0) { - result = hashPartMap->hashElements[hit].partitionOid; - } + result = hashPartMap->hashElements[hit].partitionOid; decre_partmap_refcount(partMap); return result; diff --git a/src/gausskernel/optimizer/util/pruning.cpp b/src/gausskernel/optimizer/util/pruning.cpp index 18e46ae61..bf7c9ecb7 100644 --- a/src/gausskernel/optimizer/util/pruning.cpp +++ b/src/gausskernel/optimizer/util/pruning.cpp @@ -990,8 +990,21 @@ static PruningResult* partitionPruningFromNullTest(PartitionType partType, NullT return result; } if (expr->nulltesttype != IS_NULL) { + /* For IS_NOT_NULL, we don't try to verify that the first partition is all NULL, + * so do not perform pruning here. + */ result->state = PRUNING_RESULT_FULL; } else { + if (DB_IS_CMPT(A_FORMAT)) { + /* Clause hash partition for null value always route to first partition, + * so for IS_NULL, we return the first partition. + */ + result->isPbeSinlePartition = true; + result->boundary = NULL; + result->state = PRUNING_RESULT_SUBSET; + result->bm_rangeSelectedPartitions = bms_make_singleton(0); + return result; + } result->state = PRUNING_RESULT_EMPTY; } result->isPbeSinlePartition = false; diff --git a/src/test/regress/expected/hw_partition_hash_dql.out b/src/test/regress/expected/hw_partition_hash_dql.out index 0dbddbef4..c80fcf03a 100644 --- a/src/test/regress/expected/hw_partition_hash_dql.out +++ b/src/test/regress/expected/hw_partition_hash_dql.out @@ -24,15 +24,18 @@ partition by hash (a) insert into test_partition_for_null_hash values (0, 0, 0, 0); insert into test_partition_for_null_hash values (1, 1, 1, 1); insert into test_partition_for_null_hash values (5, 5, 5, 5); --- failed: inserted partition key does not map to any table partition +-- success insert into test_partition_for_null_hash values (null, null, null, null); -ERROR: inserted partition key does not map to any table partition -- success insert into test_partition_for_null_hash values (0, null, null, null); --- failed: The partition number is invalid or out-of-range +-- success select * from test_partition_for_null_hash partition for (null) order by 1, 2, 3, 4; -ERROR: Cannot find partition by the value -DETAIL: N/A. + a | b | c | d +---+---+---+--- + 1 | 1 | 1 | 1 + | | | +(2 rows) + -- success select * from test_partition_for_null_hash partition for (0) order by 1, 2, 3, 4; a | b | c | d @@ -41,17 +44,17 @@ select * from test_partition_for_null_hash partition for (0) order by 1, 2, 3, 4 0 | | | (2 rows) --- failed: The partition number is invalid or out-of-range -alter table test_partition_for_null_hash rename partition for (null) to test_partition_for_null_hash_part1; -ERROR: The partition number is invalid or out-of-range -- success +alter table test_partition_for_null_hash rename partition for (null) to test_partition_for_null_hash_part1; +-- failed, same partition name test_partition_for_null_hash_part1 is already exists alter table test_partition_for_null_hash rename partition for (0) to test_partition_for_null_hash_part1; +ERROR: partition "test_partition_for_null_hash_part1" of relation "test_partition_for_null_hash" already exists -- success select * from test_partition_for_null_hash partition (test_partition_for_null_hash_part1) order by 1, 2, 3, 4; a | b | c | d ---+---+---+--- - 0 | 0 | 0 | 0 - 0 | | | + 1 | 1 | 1 | 1 + | | | (2 rows) alter table test_partition_for_null_hash drop partition for (NULL); @@ -61,10 +64,10 @@ ERROR: Droping hash partition is unsupported. CREATE TABLE select_hash_partition_table_000_3( C_CHAR_1 CHAR(1), C_CHAR_2 CHAR(10), - C_CHAR_3 CHAR(102400), + C_CHAR_3 CHAR(20), C_VARCHAR_1 VARCHAR(1), C_VARCHAR_2 VARCHAR(10), - C_VARCHAR_3 VARCHAR(1024), + C_VARCHAR_3 VARCHAR(20), C_INT INTEGER, C_BIGINT BIGINT, C_SMALLINT SMALLINT, @@ -95,10 +98,17 @@ INSERT INTO select_hash_partition_table_000_3 VALUES('I','IJK','IJKLMNO','i','ij INSERT INTO select_hash_partition_table_000_3 VALUES('I','IJK','IJKLMNO','i','ijk','ijklmno',1100,999999,99,9.9,9.99,9.999,'2000-09-09','2000-09-09 09:09:09','2000-09-09 09:09:09+09'); INSERT INTO select_hash_partition_table_000_3 VALUES('I','IJK','IJKLMNO','i','ijk','ijklmno',1600,999999,99,9.9,9.99,9.999,'2000-09-09','2000-09-09 09:09:09','2000-09-09 09:09:09+09'); select * from select_hash_partition_table_000_3 partition for (NULL) order by C_INT; -ERROR: Cannot find partition by the value -DETAIL: N/A. + c_char_1 | c_char_2 | c_char_3 | c_varchar_1 | c_varchar_2 | c_varchar_3 | c_int | c_bigint | c_smallint | c_float | c_numeric | c_dp | c_date | c_ts_without | c_ts_with +----------+------------+----------------------+-------------+-------------+-------------+-------+----------+------------+---------+-----------+-------+--------------------------+--------------------------+------------------------------ + A | ABC | ABCDEFG | a | abc | abcdefg | 111 | 111111 | 11 | 1.1 | 1.11000 | 1.111 | Sat Jan 01 00:00:00 2000 | Sat Jan 01 01:01:01 2000 | Fri Dec 31 16:01:01 1999 PST + E | EFG | EFGHIJK | e | efg | efghijk | 555 | 555555 | 55 | 5.5 | 5.55000 | 5.555 | Fri May 05 00:00:00 2000 | Fri May 05 05:05:05 2000 | Thu May 04 17:05:05 2000 PDT + F | FGH | FGHIJKL | f | fgh | fghijkl | 666 | 666666 | 66 | 6.6 | 6.66000 | 6.666 | Tue Jun 06 00:00:00 2000 | Tue Jun 06 06:06:06 2000 | Mon Jun 05 17:06:06 2000 PDT + G | GHI | GHIJKLM | g | ghi | ghijklm | 777 | 777777 | 77 | 7.7 | 7.77000 | 7.777 | Fri Jul 07 00:00:00 2000 | Fri Jul 07 07:07:07 2000 | Thu Jul 06 17:07:07 2000 PDT + I | IJK | IJKLMNO | i | ijk | ijklmno | 999 | 999999 | 99 | 9.9 | 9.99000 | 9.999 | Sat Sep 09 00:00:00 2000 | Sat Sep 09 09:09:09 2000 | Fri Sep 08 17:09:09 2000 PDT + I | IJK | IJKLMNO | i | ijk | ijklmno | 999 | 999999 | 99 | 9.9 | 9.99000 | 9.999 | Sat Sep 09 00:00:00 2000 | Sat Sep 09 09:09:09 2000 | Fri Sep 08 17:09:09 2000 PDT +(6 rows) + alter table select_hash_partition_table_000_3 rename partition for (NULL) to select_hash_partition_table_000_3_p1; -ERROR: The partition number is invalid or out-of-range alter table select_hash_partition_table_000_3 drop partition for (NULL); ERROR: Droping hash partition is unsupported. CREATE TABLE partition_wise_join_table_001_1 (ID INT NOT NULL,NAME VARCHAR(50) NOT NULL,SCORE NUMERIC(4,1),BIRTHDAY TIMESTAMP WITHOUT TIME ZONE,ADDRESS TEXT,SALARY double precision,RANK SMALLINT) diff --git a/src/test/regress/expected/hw_partition_hash_insert.out b/src/test/regress/expected/hw_partition_hash_insert.out index bde9cddec..32042ff64 100644 --- a/src/test/regress/expected/hw_partition_hash_insert.out +++ b/src/test/regress/expected/hw_partition_hash_insert.out @@ -22,9 +22,8 @@ select * from test_partition_for_null_hash order by a; 5 | 5 | 5 | 5 (6 rows) --- failed: inserted partition key does not map to any table partition +-- success insert into test_partition_for_null_hash values (null, null, null, null); -ERROR: inserted partition key does not map to any table partition -- success insert into test_partition_for_null_hash values (0, null, null, null); CREATE TABLE select_hash_partition_table_000_3( @@ -180,6 +179,108 @@ select count(*) from hw_partition_select_test; 4 (1 row) +create table hw_hash_partition_inert_null (c1 int, c2 timestamp) +partition by hash (c2) +( + partition hw_hash_partition_inert_null_p1, + partition hw_hash_partition_inert_null_p2, + partition hw_hash_partition_inert_null_p3, + partition hw_hash_partition_inert_null_p4, + partition hw_hash_partition_inert_null_p5 +); +insert into hw_hash_partition_inert_null values (generate_series(1, 1000), null); +insert into hw_hash_partition_inert_null values (1001, '2025-02-24 00:00:00'::timestamp); +insert into hw_hash_partition_inert_null values (1002, '2024-01-01 00:00:00'::timestamp); +insert into hw_hash_partition_inert_null values (1003, '2024-06-06 00:00:00'::timestamp); +select p.relname, ta.row_count from (select tableoid::regclass as partition_id, count(*) as row_count from hw_hash_partition_inert_null group by partition_id) as ta left join pg_partition p on p.oid = ta.partition_id order by p.oid; + relname | row_count +---------------------------------+----------- + hw_hash_partition_inert_null_p1 | 1000 + hw_hash_partition_inert_null_p2 | 1 + hw_hash_partition_inert_null_p3 | 1 + hw_hash_partition_inert_null_p4 | 1 +(4 rows) + +drop table hw_hash_partition_inert_null; +-- non index partition key +drop table if exists t1_part; +NOTICE: table "t1_part" does not exist, skipping +create table t1_part (c1 int, c2 int) partition by hash (c2) partitions 6; +insert into t1_part values (generate_series(1, 1000), null); +insert into t1_part values (1001, generate_series(1, 100)); +explain (costs off) select * from t1_part where c2 is null; + QUERY PLAN +--------------------------------- + Partitioned Seq Scan on t1_part + Filter: (c2 IS NULL) + Selected Partitions: 1 +(3 rows) + +explain (costs off) select * from t1_part where c2 = 20; + QUERY PLAN +--------------------------------- + Partitioned Seq Scan on t1_part + Filter: (c2 = 20) + Selected Partitions: 5 +(3 rows) + +drop table t1_part; +-- local index partition key +drop table if exists t1_partindex; +NOTICE: table "t1_partindex" does not exist, skipping +create table t1_partindex (c1 int, c2 int) partition by hash (c2) partitions 6; +create index t1_partindex_c2_ind on t1_partindex (c2) local; +insert into t1_partindex values (generate_series(1, 1000), null); +insert into t1_partindex values (2000, generate_series(1, 100)); +explain (costs off) select * from t1_partindex where c2 is null; + QUERY PLAN +------------------------------------------------------------ + Partitioned Bitmap Heap Scan on t1_partindex + Recheck Cond: (c2 IS NULL) + Selected Partitions: 1 + -> Partitioned Bitmap Index Scan on t1_partindex_c2_ind + Index Cond: (c2 IS NULL) + Selected Partitions: 1 +(6 rows) + +explain (costs off) select * from t1_partindex where c2 = 20; + QUERY PLAN +------------------------------------------------------------ + Partitioned Bitmap Heap Scan on t1_partindex + Recheck Cond: (c2 = 20) + Selected Partitions: 5 + -> Partitioned Bitmap Index Scan on t1_partindex_c2_ind + Index Cond: (c2 = 20) + Selected Partitions: 5 +(6 rows) + +drop table t1_partindex; +-- global index partition key +drop table if exists t1_partindex_global; +NOTICE: table "t1_partindex_global" does not exist, skipping +create table t1_partindex_global (c1 int, c2 int) partition by hash (c2) partitions 6; +create index t1_partindex_global_c2_ind on t1_partindex_global (c2); +insert into t1_partindex_global values (generate_series(1, 1000), null); +insert into t1_partindex_global values (2000, generate_series(1, 100)); +explain (costs off) select * from t1_partindex_global where c2 is null; + QUERY PLAN +------------------------------------------------------- + Bitmap Heap Scan on t1_partindex_global + Recheck Cond: (c2 IS NULL) + -> Bitmap Index Scan on t1_partindex_global_c2_ind + Index Cond: (c2 IS NULL) +(4 rows) + +explain (costs off) select * from t1_partindex_global where c2 = 20; + QUERY PLAN +------------------------------------------------------- + Bitmap Heap Scan on t1_partindex_global + Recheck Cond: (c2 = 20) + -> Bitmap Index Scan on t1_partindex_global_c2_ind + Index Cond: (c2 = 20) +(4 rows) + +drop table t1_partindex_global; drop schema FVT_COMPRESS_QWER cascade; NOTICE: drop cascades to 9 other objects DETAIL: drop cascades to table test_partition_for_null_hash diff --git a/src/test/regress/sql/hw_partition_hash_dql.sql b/src/test/regress/sql/hw_partition_hash_dql.sql index 151ee78de..b38efd3f5 100644 --- a/src/test/regress/sql/hw_partition_hash_dql.sql +++ b/src/test/regress/sql/hw_partition_hash_dql.sql @@ -31,21 +31,21 @@ insert into test_partition_for_null_hash values (0, 0, 0, 0); insert into test_partition_for_null_hash values (1, 1, 1, 1); insert into test_partition_for_null_hash values (5, 5, 5, 5); --- failed: inserted partition key does not map to any table partition +-- success insert into test_partition_for_null_hash values (null, null, null, null); -- success insert into test_partition_for_null_hash values (0, null, null, null); --- failed: The partition number is invalid or out-of-range +-- success select * from test_partition_for_null_hash partition for (null) order by 1, 2, 3, 4; -- success select * from test_partition_for_null_hash partition for (0) order by 1, 2, 3, 4; --- failed: The partition number is invalid or out-of-range -alter table test_partition_for_null_hash rename partition for (null) to test_partition_for_null_hash_part1; -- success +alter table test_partition_for_null_hash rename partition for (null) to test_partition_for_null_hash_part1; +-- failed, same partition name test_partition_for_null_hash_part1 is already exists alter table test_partition_for_null_hash rename partition for (0) to test_partition_for_null_hash_part1; -- success select * from test_partition_for_null_hash partition (test_partition_for_null_hash_part1) order by 1, 2, 3, 4; @@ -56,10 +56,10 @@ alter table test_partition_for_null_hash drop partition for (0); CREATE TABLE select_hash_partition_table_000_3( C_CHAR_1 CHAR(1), C_CHAR_2 CHAR(10), - C_CHAR_3 CHAR(102400), + C_CHAR_3 CHAR(20), C_VARCHAR_1 VARCHAR(1), C_VARCHAR_2 VARCHAR(10), - C_VARCHAR_3 VARCHAR(1024), + C_VARCHAR_3 VARCHAR(20), C_INT INTEGER, C_BIGINT BIGINT, C_SMALLINT SMALLINT, diff --git a/src/test/regress/sql/hw_partition_hash_insert.sql b/src/test/regress/sql/hw_partition_hash_insert.sql index 4add4d4bb..f3cc577d4 100644 --- a/src/test/regress/sql/hw_partition_hash_insert.sql +++ b/src/test/regress/sql/hw_partition_hash_insert.sql @@ -13,7 +13,7 @@ insert into test_partition_for_null_hash values (1, 1, 1, 1); insert into test_partition_for_null_hash values (5, 5, 5, 5); insert into test_partition_for_null_hash select * from test_partition_for_null_hash; select * from test_partition_for_null_hash order by a; --- failed: inserted partition key does not map to any table partition +-- success insert into test_partition_for_null_hash values (null, null, null, null); -- success insert into test_partition_for_null_hash values (0, null, null, null); @@ -148,4 +148,58 @@ insert into hw_partition_select_test values(555); insert into hw_partition_select_test values(888); insert into hw_partition_select_test values(100); select count(*) from hw_partition_select_test; + +create table hw_hash_partition_inert_null (c1 int, c2 timestamp) +partition by hash (c2) +( + partition hw_hash_partition_inert_null_p1, + partition hw_hash_partition_inert_null_p2, + partition hw_hash_partition_inert_null_p3, + partition hw_hash_partition_inert_null_p4, + partition hw_hash_partition_inert_null_p5 +); +insert into hw_hash_partition_inert_null values (generate_series(1, 1000), null); +insert into hw_hash_partition_inert_null values (1001, '2025-02-24 00:00:00'::timestamp); +insert into hw_hash_partition_inert_null values (1002, '2024-01-01 00:00:00'::timestamp); +insert into hw_hash_partition_inert_null values (1003, '2024-06-06 00:00:00'::timestamp); +select p.relname, ta.row_count from (select tableoid::regclass as partition_id, count(*) as row_count from hw_hash_partition_inert_null group by partition_id) as ta left join pg_partition p on p.oid = ta.partition_id order by p.oid; +drop table hw_hash_partition_inert_null; + +-- non index partition key +drop table if exists t1_part; +create table t1_part (c1 int, c2 int) partition by hash (c2) partitions 6; +insert into t1_part values (generate_series(1, 1000), null); +insert into t1_part values (1001, generate_series(1, 100)); + +explain (costs off) select * from t1_part where c2 is null; +explain (costs off) select * from t1_part where c2 = 20; + +drop table t1_part; + +-- local index partition key +drop table if exists t1_partindex; +create table t1_partindex (c1 int, c2 int) partition by hash (c2) partitions 6; +create index t1_partindex_c2_ind on t1_partindex (c2) local; + +insert into t1_partindex values (generate_series(1, 1000), null); +insert into t1_partindex values (2000, generate_series(1, 100)); + +explain (costs off) select * from t1_partindex where c2 is null; +explain (costs off) select * from t1_partindex where c2 = 20; + +drop table t1_partindex; + +-- global index partition key +drop table if exists t1_partindex_global; +create table t1_partindex_global (c1 int, c2 int) partition by hash (c2) partitions 6; +create index t1_partindex_global_c2_ind on t1_partindex_global (c2); + +insert into t1_partindex_global values (generate_series(1, 1000), null); +insert into t1_partindex_global values (2000, generate_series(1, 100)); + +explain (costs off) select * from t1_partindex_global where c2 is null; +explain (costs off) select * from t1_partindex_global where c2 = 20; + +drop table t1_partindex_global; + drop schema FVT_COMPRESS_QWER cascade; \ No newline at end of file