diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 7179b22a05..20b5e01c9e 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -167,7 +167,6 @@ static List *get_steps_using_prefix(GeneratePruningStepsContext *context, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, - int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix); static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, @@ -175,7 +174,6 @@ static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, - int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix, ListCell *start, @@ -1531,7 +1529,6 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context, pc->op_is_ne, pc->expr, pc->cmpfn, - 0, NULL, NIL); opsteps = list_concat(opsteps, pc_steps); @@ -1657,7 +1654,6 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context, pc->op_is_ne, pc->expr, pc->cmpfn, - pc->keyno, NULL, prefix); opsteps = list_concat(opsteps, pc_steps); @@ -1731,7 +1727,6 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context, false, pc->expr, pc->cmpfn, - pc->keyno, nullkeys, prefix); opsteps = list_concat(opsteps, pc_steps); @@ -2350,25 +2345,27 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context, /* * get_steps_using_prefix - * Generate list of PartitionPruneStepOp steps each consisting of given + * Generate a list of PartitionPruneStepOp steps each consisting of given * opstrategy * * To generate steps, step_lastexpr and step_lastcmpfn are appended to * expressions and cmpfns, respectively, extracted from the clauses in - * 'prefix'. Actually, since 'prefix' may contain multiple clauses for the - * same partition key column, we must generate steps for various combinations - * of the clauses of different keys. + * 'prefix'. Since 'prefix' may contain multiple clauses for each partition + * key, and since each step can only contain a single clause for each + * partition key, when there are multiple clauses for any given key, we must + * generate steps for all combinations of the clauses. * * For list/range partitioning, callers must ensure that step_nullkeys is * NULL, and that prefix contains at least one clause for each of the - * partition keys earlier than one specified in step_lastkeyno if it's - * greater than zero. For hash partitioning, step_nullkeys is allowed to be - * non-NULL, but they must ensure that prefix contains at least one clause - * for each of the partition keys other than those specified in step_nullkeys - * and step_lastkeyno. - * - * For both cases, callers must also ensure that clauses in prefix are sorted - * in ascending order of their partition key numbers. + * partition keys prior to the key that 'step_lastexpr' belongs to. + * + * For hash partitioning, callers must ensure that 'prefix' contains at least + * one clause for each of the partition keys apart from the final key. A bit + * set in step_nullkeys can substitute clauses in the 'prefix' list for any + * given key. Both may not be specified. + * + * For each of the above cases, callers must ensure that PartClauseInfos in + * 'prefix' are sorted in ascending order of keyno. */ static List * get_steps_using_prefix(GeneratePruningStepsContext *context, @@ -2376,7 +2373,6 @@ get_steps_using_prefix(GeneratePruningStepsContext *context, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, - int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix) { @@ -2397,13 +2393,12 @@ get_steps_using_prefix(GeneratePruningStepsContext *context, return list_make1(step); } - /* Recurse to generate steps for various combinations. */ + /* Recurse to generate steps for every combination of clauses. */ return get_steps_using_prefix_recurse(context, step_opstrategy, step_op_is_ne, step_lastexpr, step_lastcmpfn, - step_lastkeyno, step_nullkeys, prefix, list_head(prefix), @@ -2413,9 +2408,8 @@ get_steps_using_prefix(GeneratePruningStepsContext *context, /* * get_steps_using_prefix_recurse * Recursively generate combinations of clauses for different partition - * keys and start generating steps upon reaching clauses for the greatest - * column that is less than the one for which we're currently generating - * steps (that is, step_lastkeyno) + * keys and generate PartitionPruneSteps for each combination of + * PartClauseInfos in the 'prefix' list. * * 'prefix' is the list of PartClauseInfos. * 'start' is where we should start iterating for the current invocation. @@ -2428,7 +2422,6 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, - int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix, ListCell *start, @@ -2438,14 +2431,17 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, List *result = NIL; ListCell *lc; int cur_keyno; + int final_keyno; /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */ check_stack_depth(); - /* Check if we need to recurse. */ Assert(start != NULL); cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno; - if (cur_keyno < step_lastkeyno - 1) + final_keyno = ((PartClauseInfo *) llast(prefix))->keyno; + + /* Check if we need to recurse. */ + if (cur_keyno < final_keyno) { PartClauseInfo *pc; ListCell *next_start; @@ -2493,7 +2489,6 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, step_op_is_ne, step_lastexpr, step_lastcmpfn, - step_lastkeyno, step_nullkeys, prefix, next_start, diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 36791293ee..1bfdf37657 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -4024,20 +4024,327 @@ explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b Filter: ((a >= 1) AND (b >= 1) AND (d >= 0) AND (b = 2) AND (c = 2)) (2 rows) +drop table rp_prefix_test1; +drop table rp_prefix_test2; +drop table rp_prefix_test3; create table hp_prefix_test (a int, b int, c int, d int) partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops); -create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 2, remainder 0); -create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 2, remainder 1); --- Test that get_steps_using_prefix() handles non-NULL step_nullkeys -explain (costs off) select * from hp_prefix_test where a = 1 and b is null and c = 1 and d = 1; +-- create 16 partitions +select 'create table hp_prefix_test_p' || x::text || ' partition of hp_prefix_test for values with (modulus 16, remainder ' || x::text || ');' +from generate_Series(0,15) x; + ?column? +--------------------------------------------------------------------------------------------------------- + create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 16, remainder 0); + create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 16, remainder 1); + create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 16, remainder 2); + create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 16, remainder 3); + create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 16, remainder 4); + create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 16, remainder 5); + create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 16, remainder 6); + create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 16, remainder 7); + create table hp_prefix_test_p8 partition of hp_prefix_test for values with (modulus 16, remainder 8); + create table hp_prefix_test_p9 partition of hp_prefix_test for values with (modulus 16, remainder 9); + create table hp_prefix_test_p10 partition of hp_prefix_test for values with (modulus 16, remainder 10); + create table hp_prefix_test_p11 partition of hp_prefix_test for values with (modulus 16, remainder 11); + create table hp_prefix_test_p12 partition of hp_prefix_test for values with (modulus 16, remainder 12); + create table hp_prefix_test_p13 partition of hp_prefix_test for values with (modulus 16, remainder 13); + create table hp_prefix_test_p14 partition of hp_prefix_test for values with (modulus 16, remainder 14); + create table hp_prefix_test_p15 partition of hp_prefix_test for values with (modulus 16, remainder 15); +(16 rows) + +\gexec +create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 16, remainder 0); +create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 16, remainder 1); +create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 16, remainder 2); +create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 16, remainder 3); +create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 16, remainder 4); +create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 16, remainder 5); +create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 16, remainder 6); +create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 16, remainder 7); +create table hp_prefix_test_p8 partition of hp_prefix_test for values with (modulus 16, remainder 8); +create table hp_prefix_test_p9 partition of hp_prefix_test for values with (modulus 16, remainder 9); +create table hp_prefix_test_p10 partition of hp_prefix_test for values with (modulus 16, remainder 10); +create table hp_prefix_test_p11 partition of hp_prefix_test for values with (modulus 16, remainder 11); +create table hp_prefix_test_p12 partition of hp_prefix_test for values with (modulus 16, remainder 12); +create table hp_prefix_test_p13 partition of hp_prefix_test for values with (modulus 16, remainder 13); +create table hp_prefix_test_p14 partition of hp_prefix_test for values with (modulus 16, remainder 14); +create table hp_prefix_test_p15 partition of hp_prefix_test for values with (modulus 16, remainder 15); +-- insert one row for each test to perform. +insert into hp_prefix_test +select + case a when 0 then null else 1 end, + case b when 0 then null else 2 end, + case c when 0 then null else 3 end, + case d when 0 then null else 4 end +from + generate_series(0,1) a, + generate_series(0,1) b, + generate_Series(0,1) c, + generate_Series(0,1) d; +-- Ensure partition pruning works correctly for each combination of IS NULL +-- and equality quals. +select + 'explain (costs off) select tableoid::regclass,* from hp_prefix_test where ' || + string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) +from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) +group by g.s +order by g.s; + ?column? +------------------------------------------------------------------------------------------------------------------------------- + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 +(16 rows) + +\gexec +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + QUERY PLAN +------------------------------------------------------------------------- + Seq Scan on hp_prefix_test_p0 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (c IS NULL) AND (d IS NULL)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null + QUERY PLAN +--------------------------------------------------------------------- + Seq Scan on hp_prefix_test_p1 hp_prefix_test + Filter: ((b IS NULL) AND (c IS NULL) AND (d IS NULL) AND (a = 1)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + QUERY PLAN +--------------------------------------------------------------------- + Seq Scan on hp_prefix_test_p2 hp_prefix_test + Filter: ((a IS NULL) AND (c IS NULL) AND (d IS NULL) AND (b = 2)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on hp_prefix_test_p12 hp_prefix_test + Filter: ((c IS NULL) AND (d IS NULL) AND (a = 1) AND (b = 2)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + QUERY PLAN +--------------------------------------------------------------------- + Seq Scan on hp_prefix_test_p3 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (d IS NULL) AND (c = 3)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on hp_prefix_test_p15 hp_prefix_test + Filter: ((b IS NULL) AND (d IS NULL) AND (a = 1) AND (c = 3)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on hp_prefix_test_p12 hp_prefix_test + Filter: ((a IS NULL) AND (d IS NULL) AND (b = 2) AND (c = 3)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null QUERY PLAN ------------------------------------------------------------- - Seq Scan on hp_prefix_test_p1 hp_prefix_test - Filter: ((b IS NULL) AND (a = 1) AND (c = 1) AND (d = 1)) + Seq Scan on hp_prefix_test_p5 hp_prefix_test + Filter: ((d IS NULL) AND (a = 1) AND (b = 2) AND (c = 3)) (2 rows) -drop table rp_prefix_test1; -drop table rp_prefix_test2; -drop table rp_prefix_test3; +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + QUERY PLAN +--------------------------------------------------------------------- + Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (c IS NULL) AND (d = 4)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on hp_prefix_test_p14 hp_prefix_test + Filter: ((b IS NULL) AND (c IS NULL) AND (a = 1) AND (d = 4)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on hp_prefix_test_p13 hp_prefix_test + Filter: ((a IS NULL) AND (c IS NULL) AND (b = 2) AND (d = 4)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + QUERY PLAN +------------------------------------------------------------- + Seq Scan on hp_prefix_test_p6 hp_prefix_test + Filter: ((c IS NULL) AND (a = 1) AND (b = 2) AND (d = 4)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + QUERY PLAN +----------------------------------------------------------------- + Seq Scan on hp_prefix_test_p12 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (c = 3) AND (d = 4)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + QUERY PLAN +------------------------------------------------------------- + Seq Scan on hp_prefix_test_p5 hp_prefix_test + Filter: ((b IS NULL) AND (a = 1) AND (c = 3) AND (d = 4)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + QUERY PLAN +------------------------------------------------------------- + Seq Scan on hp_prefix_test_p6 hp_prefix_test + Filter: ((a IS NULL) AND (b = 2) AND (c = 3) AND (d = 4)) +(2 rows) + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 + QUERY PLAN +--------------------------------------------------------- + Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((a = 1) AND (b = 2) AND (c = 3) AND (d = 4)) +(2 rows) + +-- And ensure we get exactly 1 row from each. +select + 'select tableoid::regclass,* from hp_prefix_test where ' || + string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) +from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) +group by g.s +order by g.s; + ?column? +----------------------------------------------------------------------------------------------------------- + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 +(16 rows) + +\gexec +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + tableoid | a | b | c | d +-------------------+---+---+---+--- + hp_prefix_test_p0 | | | | +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null + tableoid | a | b | c | d +-------------------+---+---+---+--- + hp_prefix_test_p1 | 1 | | | +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + tableoid | a | b | c | d +-------------------+---+---+---+--- + hp_prefix_test_p2 | | 2 | | +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + tableoid | a | b | c | d +--------------------+---+---+---+--- + hp_prefix_test_p12 | 1 | 2 | | +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + tableoid | a | b | c | d +-------------------+---+---+---+--- + hp_prefix_test_p3 | | | 3 | +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + tableoid | a | b | c | d +--------------------+---+---+---+--- + hp_prefix_test_p15 | 1 | | 3 | +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + tableoid | a | b | c | d +--------------------+---+---+---+--- + hp_prefix_test_p12 | | 2 | 3 | +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + tableoid | a | b | c | d +-------------------+---+---+---+--- + hp_prefix_test_p5 | 1 | 2 | 3 | +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + tableoid | a | b | c | d +-------------------+---+---+---+--- + hp_prefix_test_p4 | | | | 4 +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + tableoid | a | b | c | d +--------------------+---+---+---+--- + hp_prefix_test_p14 | 1 | | | 4 +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + tableoid | a | b | c | d +--------------------+---+---+---+--- + hp_prefix_test_p13 | | 2 | | 4 +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + tableoid | a | b | c | d +-------------------+---+---+---+--- + hp_prefix_test_p6 | 1 | 2 | | 4 +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + tableoid | a | b | c | d +--------------------+---+---+---+--- + hp_prefix_test_p12 | | | 3 | 4 +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + tableoid | a | b | c | d +-------------------+---+---+---+--- + hp_prefix_test_p5 | 1 | | 3 | 4 +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + tableoid | a | b | c | d +-------------------+---+---+---+--- + hp_prefix_test_p6 | | 2 | 3 | 4 +(1 row) + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 + tableoid | a | b | c | d +-------------------+---+---+---+--- + hp_prefix_test_p4 | 1 | 2 | 3 | 4 +(1 row) + drop table hp_prefix_test; -- -- Check that gen_partprune_steps() detects self-contradiction from clauses diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index d23133fe43..6b4039179f 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -1188,16 +1188,49 @@ explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b -- that the caller arranges clauses in that prefix in the required order) explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b = 2 and c = 2 and d >= 0; -create table hp_prefix_test (a int, b int, c int, d int) partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops); -create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 2, remainder 0); -create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 2, remainder 1); - --- Test that get_steps_using_prefix() handles non-NULL step_nullkeys -explain (costs off) select * from hp_prefix_test where a = 1 and b is null and c = 1 and d = 1; - drop table rp_prefix_test1; drop table rp_prefix_test2; drop table rp_prefix_test3; + +create table hp_prefix_test (a int, b int, c int, d int) partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops); + +-- create 16 partitions +select 'create table hp_prefix_test_p' || x::text || ' partition of hp_prefix_test for values with (modulus 16, remainder ' || x::text || ');' +from generate_Series(0,15) x; +\gexec + +-- insert one row for each test to perform. +insert into hp_prefix_test +select + case a when 0 then null else 1 end, + case b when 0 then null else 2 end, + case c when 0 then null else 3 end, + case d when 0 then null else 4 end +from + generate_series(0,1) a, + generate_series(0,1) b, + generate_Series(0,1) c, + generate_Series(0,1) d; + +-- Ensure partition pruning works correctly for each combination of IS NULL +-- and equality quals. +select + 'explain (costs off) select tableoid::regclass,* from hp_prefix_test where ' || + string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) +from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) +group by g.s +order by g.s; +\gexec + +-- And ensure we get exactly 1 row from each. +select + 'select tableoid::regclass,* from hp_prefix_test where ' || + string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) +from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) +group by g.s +order by g.s; +\gexec + drop table hp_prefix_test; --