Compare commits

..

No commits in common. "1e81d3e6e0a0e35e52e109742c5ce3738c3e87c6" and "5143f764a74adb830b801ad8425ef78ee088573a" have entirely different histories.

5 changed files with 93 additions and 389 deletions

View File

@ -5278,14 +5278,13 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
<listitem>
<para>
Enables or disables the query planner's use of partitionwise grouping
or aggregation, which allows grouping or aggregation on partitioned
tables to be performed separately for each partition. If the
<literal>GROUP BY</literal> clause does not include the partition
keys, only partial aggregation can be performed on a per-partition
basis, and finalization must be performed later. Because
partitionwise grouping or aggregation can use significantly more CPU
time and memory during planning, the default is
<literal>off</literal>.
or aggregation, which allows grouping or aggregation on a partitioned
tables performed separately for each partition. If the <literal>GROUP
BY</literal> clause does not include the partition keys, only partial
aggregation can be performed on a per-partition basis, and
finalization must be performed later. Because partitionwise grouping
or aggregation can use significantly more CPU time and memory during
planning, the default is <literal>off</literal>.
</para>
</listitem>
</varlistentry>

View File

@ -1956,7 +1956,7 @@ InitPartitionPruneContext(PartitionPruneContext *context,
foreach(lc, pruning_steps)
{
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
ListCell *lc2 = list_head(step->exprs);
ListCell *lc2;
int keyno;
/* not needed for other step kinds */
@ -1965,39 +1965,34 @@ InitPartitionPruneContext(PartitionPruneContext *context,
Assert(list_length(step->exprs) <= partnatts);
for (keyno = 0; keyno < partnatts; keyno++)
keyno = 0;
foreach(lc2, step->exprs)
{
if (bms_is_member(keyno, step->nullkeys))
continue;
Expr *expr = (Expr *) lfirst(lc2);
if (lc2 != NULL)
/* not needed for Consts */
if (!IsA(expr, Const))
{
Expr *expr = lfirst(lc2);
int stateidx = PruneCxtStateIdx(partnatts,
step->step.step_id,
keyno);
/* not needed for Consts */
if (!IsA(expr, Const))
{
int stateidx = PruneCxtStateIdx(partnatts,
step->step.step_id,
keyno);
/*
* When planstate is NULL, pruning_steps is known not to
* contain any expressions that depend on the parent plan.
* Information of any available EXTERN parameters must be
* passed explicitly in that case, which the caller must
* have made available via econtext.
*/
if (planstate == NULL)
context->exprstates[stateidx] =
ExecInitExprWithParams(expr,
econtext->ecxt_param_list_info);
else
context->exprstates[stateidx] =
ExecInitExpr(expr, context->planstate);
}
lc2 = lnext(step->exprs, lc2);
/*
* When planstate is NULL, pruning_steps is known not to
* contain any expressions that depend on the parent plan.
* Information of any available EXTERN parameters must be
* passed explicitly in that case, which the caller must have
* made available via econtext.
*/
if (planstate == NULL)
context->exprstates[stateidx] =
ExecInitExprWithParams(expr,
econtext->ecxt_param_list_info);
else
context->exprstates[stateidx] =
ExecInitExpr(expr, context->planstate);
}
keyno++;
}
}
}

View File

@ -167,6 +167,7 @@ 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,
@ -174,6 +175,7 @@ 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,
@ -1529,6 +1531,7 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
pc->op_is_ne,
pc->expr,
pc->cmpfn,
0,
NULL,
NIL);
opsteps = list_concat(opsteps, pc_steps);
@ -1654,6 +1657,7 @@ 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);
@ -1727,6 +1731,7 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
false,
pc->expr,
pc->cmpfn,
pc->keyno,
nullkeys,
prefix);
opsteps = list_concat(opsteps, pc_steps);
@ -2346,31 +2351,25 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
/*
* get_steps_using_prefix
* Generate a list of PartitionPruneStepOps based on the given input.
* Generate list of PartitionPruneStepOp steps each consisting of given
* opstrategy
*
* 'step_lastexpr' and 'step_lastcmpfn' are the Expr and comparison function
* belonging to the final partition key that we have a clause for. 'prefix'
* is a list of PartClauseInfos for partition key numbers prior to the given
* 'step_lastexpr' and 'step_lastcmpfn'. 'prefix' may contain multiple
* PartClauseInfos belonging to a single partition key. We will generate a
* PartitionPruneStepOp for each combination of the given PartClauseInfos
* using, at most, one PartClauseInfo per partition key.
* 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.
*
* For LIST and RANGE partitioned tables, callers must ensure that
* step_nullkeys is NULL, and that prefix contains at least one clause for
* each of the partition keys prior to the key that 'step_lastexpr' and
* 'step_lastcmpfn'belong to.
* 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 HASH partitioned tables, callers must ensure that 'prefix' contains at
* least one clause for each of the partition keys apart from the final key
* (the expr and comparison function for the final key are in 'step_lastexpr'
* and 'step_lastcmpfn'). A bit set in step_nullkeys can substitute clauses
* in the 'prefix' list for any given key. If a bit is set in 'step_nullkeys'
* for a given key, then there must be no PartClauseInfo for that key in the
* 'prefix' list.
*
* For each of the above cases, callers must ensure that PartClauseInfos in
* 'prefix' are sorted in ascending order of keyno.
* For both cases, callers must also ensure that clauses in prefix are sorted
* in ascending order of their partition key numbers.
*/
static List *
get_steps_using_prefix(GeneratePruningStepsContext *context,
@ -2378,17 +2377,14 @@ 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)
{
/* step_nullkeys must be empty for RANGE and LIST partitioned tables */
Assert(step_nullkeys == NULL ||
context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
/*
* No recursive processing is required when 'prefix' is an empty list. This
* occurs when there is only 1 partition key column.
*/
/* Quick exit if there are no values to prefix with. */
if (list_length(prefix) == 0)
{
PartitionPruneStep *step;
@ -2402,12 +2398,13 @@ get_steps_using_prefix(GeneratePruningStepsContext *context,
return list_make1(step);
}
/* Recurse to generate steps for every combination of clauses. */
/* Recurse to generate steps for various combinations. */
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),
@ -2416,17 +2413,13 @@ get_steps_using_prefix(GeneratePruningStepsContext *context,
/*
* get_steps_using_prefix_recurse
* Generate and return a list of PartitionPruneStepOps using the 'prefix'
* list of PartClauseInfos starting at the 'start' cell.
* 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)
*
* When 'prefix' contains multiple PartClauseInfos for a single partition key
* we create a PartitionPruneStepOp for each combination of duplicated
* PartClauseInfos. The returned list will contain a PartitionPruneStepOp
* for each unique combination of input PartClauseInfos containing at most one
* PartClauseInfo per partition key.
*
* 'prefix' is the input list of PartClauseInfos sorted by keyno.
* 'start' marks the cell that searching the 'prefix' list should start from.
* 'prefix' is the list of PartClauseInfos.
* 'start' is where we should start iterating for the current invocation.
* 'step_exprs' and 'step_cmpfns' each contains the expressions and cmpfns
* we've generated so far from the clauses for the previous part keys.
*/
@ -2436,6 +2429,7 @@ 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,
@ -2445,25 +2439,23 @@ 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;
final_keyno = ((PartClauseInfo *) llast(prefix))->keyno;
/* Check if we need to recurse. */
if (cur_keyno < final_keyno)
if (cur_keyno < step_lastkeyno - 1)
{
PartClauseInfo *pc;
ListCell *next_start;
/*
* Find the first PartClauseInfo belonging to the next partition key, the
* next recursive call must start iteration of the prefix list from that
* point.
* For each clause with cur_keyno, add its expr and cmpfn to
* step_exprs and step_cmpfns, respectively, and recurse after setting
* next_start to the ListCell of the first clause for the next
* partition key.
*/
for_each_cell(lc, prefix, start)
{
@ -2472,15 +2464,8 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
if (pc->keyno > cur_keyno)
break;
}
/* record where to start iterating in the next recursive call */
next_start = lc;
/*
* For each PartClauseInfo with keyno set to cur_keyno, add its expr and
* cmpfn to step_exprs and step_cmpfns, respectively, and recurse using
* 'next_start' as the starting point in the 'prefix' list.
*/
for_each_cell(lc, prefix, start)
{
List *moresteps;
@ -2500,7 +2485,6 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
}
else
{
/* check the 'prefix' list is sorted correctly */
Assert(pc->keyno > cur_keyno);
break;
}
@ -2510,6 +2494,7 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
step_op_is_ne,
step_lastexpr,
step_lastcmpfn,
step_lastkeyno,
step_nullkeys,
prefix,
next_start,
@ -2528,8 +2513,8 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
* each clause with cur_keyno, which is all clauses from here onward
* till the end of the list. Note that for hash partitioning,
* step_nullkeys is allowed to be non-empty, in which case step_exprs
* would only contain expressions for the partition keys that are not
* specified in step_nullkeys.
* would only contain expressions for the earlier partition keys that
* are not specified in step_nullkeys.
*/
Assert(list_length(step_exprs) == cur_keyno ||
!bms_is_empty(step_nullkeys));

View File

@ -1948,6 +1948,7 @@ explain (costs off) select * from hp where a = 1 and b = 'abcde' and
One-Time Filter: false
(2 rows)
drop table hp;
--
-- Test runtime partition pruning
--
@ -2069,27 +2070,6 @@ explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2);
Filter: ((b >= $1) AND (b <= $2) AND (a < $0))
(10 rows)
--
-- Test runtime pruning with hash partitioned tables
--
-- recreate partitions dropped above
create table hp1 partition of hp for values with (modulus 4, remainder 1);
create table hp2 partition of hp for values with (modulus 4, remainder 2);
create table hp3 partition of hp for values with (modulus 4, remainder 3);
-- Ensure we correctly prune unneeded partitions when there is an IS NULL qual
prepare hp_q1 (text) as
select * from hp where a is null and b = $1;
explain (costs off) execute hp_q1('xxx');
QUERY PLAN
--------------------------------------------
Append
Subplans Removed: 3
-> Seq Scan on hp2 hp_1
Filter: ((a IS NULL) AND (b = $1))
(4 rows)
deallocate hp_q1;
drop table hp;
-- Test a backwards Append scan
create table list_part (a int) partition by list (a);
create table list_part1 partition of list_part for values in (1);
@ -4036,217 +4016,20 @@ 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)
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;
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))
(2 rows)
drop table rp_prefix_test1;
drop table rp_prefix_test2;
drop table rp_prefix_test3;
--
-- Test that get_steps_using_prefix() handles IS NULL clauses correctly
--
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 8 partitions
select 'create table hp_prefix_test_p' || x::text || ' partition of hp_prefix_test for values with (modulus 8, remainder ' || x::text || ');'
from generate_Series(0,7) x;
?column?
------------------------------------------------------------------------------------------------------
create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 8, remainder 0);
create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 8, remainder 1);
create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 8, remainder 2);
create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 8, remainder 3);
create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 8, remainder 4);
create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 8, remainder 5);
create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 8, remainder 6);
create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 8, remainder 7);
(8 rows)
\gexec
create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 8, remainder 0);
create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 8, remainder 1);
create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 8, remainder 2);
create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 8, remainder 3);
create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 8, remainder 4);
create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 8, remainder 5);
create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 8, remainder 6);
create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 8, remainder 7);
-- insert 16 rows, 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. This may seem a little excessive, but there have been
-- a number of bugs in this area over the years. We make use of row only
-- output to reduce the size of the expected results.
\t on
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;
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
\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
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))
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
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))
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
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))
explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null
Seq Scan on hp_prefix_test_p4 hp_prefix_test
Filter: ((c IS NULL) AND (d IS NULL) AND (a = 1) AND (b = 2))
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
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))
explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null
Seq Scan on hp_prefix_test_p7 hp_prefix_test
Filter: ((b IS NULL) AND (d IS NULL) AND (a = 1) AND (c = 3))
explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null
Seq Scan on hp_prefix_test_p4 hp_prefix_test
Filter: ((a IS NULL) AND (d IS NULL) AND (b = 2) AND (c = 3))
explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null
Seq Scan on hp_prefix_test_p5 hp_prefix_test
Filter: ((d IS NULL) AND (a = 1) AND (b = 2) AND (c = 3))
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
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))
explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4
Seq Scan on hp_prefix_test_p6 hp_prefix_test
Filter: ((b IS NULL) AND (c IS NULL) AND (a = 1) 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
Seq Scan on hp_prefix_test_p5 hp_prefix_test
Filter: ((a IS NULL) AND (c IS NULL) AND (b = 2) 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
Seq Scan on hp_prefix_test_p6 hp_prefix_test
Filter: ((c IS NULL) AND (a = 1) AND (b = 2) 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
Seq Scan on hp_prefix_test_p4 hp_prefix_test
Filter: ((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
Seq Scan on hp_prefix_test_p5 hp_prefix_test
Filter: ((b IS NULL) AND (a = 1) 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
Seq Scan on hp_prefix_test_p6 hp_prefix_test
Filter: ((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
Seq Scan on hp_prefix_test_p4 hp_prefix_test
Filter: ((a = 1) AND (b = 2) AND (c = 3) AND (d = 4))
-- And ensure we get exactly 1 row from each. Again, all 16 possible combinations.
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;
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
\gexec
select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null
hp_prefix_test_p0 | | | |
select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null
hp_prefix_test_p1 | 1 | | |
select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null
hp_prefix_test_p2 | | 2 | |
select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null
hp_prefix_test_p4 | 1 | 2 | |
select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null
hp_prefix_test_p3 | | | 3 |
select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null
hp_prefix_test_p7 | 1 | | 3 |
select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null
hp_prefix_test_p4 | | 2 | 3 |
select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null
hp_prefix_test_p5 | 1 | 2 | 3 |
select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4
hp_prefix_test_p4 | | | | 4
select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4
hp_prefix_test_p6 | 1 | | | 4
select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4
hp_prefix_test_p5 | | 2 | | 4
select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4
hp_prefix_test_p6 | 1 | 2 | | 4
select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4
hp_prefix_test_p4 | | | 3 | 4
select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4
hp_prefix_test_p5 | 1 | | 3 | 4
select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4
hp_prefix_test_p6 | | 2 | 3 | 4
select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4
hp_prefix_test_p4 | 1 | 2 | 3 | 4
\t off
drop table hp_prefix_test;
--
-- Check that gen_partprune_steps() detects self-contradiction from clauses

View File

@ -384,6 +384,8 @@ drop table hp2;
explain (costs off) select * from hp where a = 1 and b = 'abcde' and
(c = 2 or c = 3);
drop table hp;
--
-- Test runtime partition pruning
--
@ -434,25 +436,6 @@ select a from ab where b between $1 and $2 and a < (select 3);
explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2);
--
-- Test runtime pruning with hash partitioned tables
--
-- recreate partitions dropped above
create table hp1 partition of hp for values with (modulus 4, remainder 1);
create table hp2 partition of hp for values with (modulus 4, remainder 2);
create table hp3 partition of hp for values with (modulus 4, remainder 3);
-- Ensure we correctly prune unneeded partitions when there is an IS NULL qual
prepare hp_q1 (text) as
select * from hp where a is null and b = $1;
explain (costs off) execute hp_q1('xxx');
deallocate hp_q1;
drop table hp;
-- Test a backwards Append scan
create table list_part (a int) partition by list (a);
create table list_part1 partition of list_part for values in (1);
@ -1199,57 +1182,16 @@ 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;
--
-- Test that get_steps_using_prefix() handles IS NULL clauses correctly
--
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 8 partitions
select 'create table hp_prefix_test_p' || x::text || ' partition of hp_prefix_test for values with (modulus 8, remainder ' || x::text || ');'
from generate_Series(0,7) x;
\gexec
-- insert 16 rows, 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. This may seem a little excessive, but there have been
-- a number of bugs in this area over the years. We make use of row only
-- output to reduce the size of the expected results.
\t on
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. Again, all 16 possible combinations.
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
\t off
drop table hp_prefix_test;
--