Compare commits

..

No commits in common. "0013ba290b6684d095d93517ff2ca1fadf79bdb9" and "e7689190b3d58404abbafe2d3312c3268a51cca3" have entirely different histories.

8 changed files with 138 additions and 462 deletions

View File

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

View File

@ -10738,13 +10738,6 @@ SELECT TIMESTAMP '2001-02-16 20:38:40' AT TIME ZONE 'Asia/Tokyo' AT TIME ZONE 'A
<literal><replaceable>timestamp</replaceable> AT TIME ZONE <literal><replaceable>timestamp</replaceable> AT TIME ZONE
<replaceable>zone</replaceable></literal>. <replaceable>zone</replaceable></literal>.
</para> </para>
<para>
The function <literal><function>timezone</function>(<replaceable>zone</replaceable>,
<replaceable>time</replaceable>)</literal> is equivalent to the SQL-conforming construct
<literal><replaceable>time</replaceable> AT TIME ZONE
<replaceable>zone</replaceable></literal>.
</para>
</sect2> </sect2>
<sect2 id="functions-datetime-current"> <sect2 id="functions-datetime-current">

View File

@ -792,26 +792,26 @@ XLogInsertRecord(XLogRecData *rdata,
*---------- *----------
*/ */
START_CRIT_SECTION(); START_CRIT_SECTION();
if (isLogSwitch)
if (likely(!isLogSwitch)) WALInsertLockAcquireExclusive();
{ else
WALInsertLockAcquire(); WALInsertLockAcquire();
/* /*
* Check to see if my copy of RedoRecPtr is out of date. If so, may * Check to see if my copy of RedoRecPtr is out of date. If so, may have
* have to go back and have the caller recompute everything. This can * to go back and have the caller recompute everything. This can only
* only happen just after a checkpoint, so it's better to be slow in * happen just after a checkpoint, so it's better to be slow in this case
* this case and fast otherwise. * and fast otherwise.
* *
* Also check to see if fullPageWrites was just turned on or there's a * Also check to see if fullPageWrites was just turned on or there's a
* running backup (which forces full-page writes); if we weren't * running backup (which forces full-page writes); if we weren't already
* already doing full-page writes then go back and recompute. * doing full-page writes then go back and recompute.
* *
* If we aren't doing full-page writes then RedoRecPtr doesn't * If we aren't doing full-page writes then RedoRecPtr doesn't actually
* actually affect the contents of the XLOG record, so we'll update * affect the contents of the XLOG record, so we'll update our local copy
* our local copy but not force a recomputation. (If doPageWrites was * but not force a recomputation. (If doPageWrites was just turned off,
* just turned off, we could recompute the record without full pages, * we could recompute the record without full pages, but we choose not to
* but we choose not to bother.) * bother.)
*/ */
if (RedoRecPtr != Insert->RedoRecPtr) if (RedoRecPtr != Insert->RedoRecPtr)
{ {
@ -825,8 +825,8 @@ XLogInsertRecord(XLogRecData *rdata,
(fpw_lsn != InvalidXLogRecPtr && fpw_lsn <= RedoRecPtr))) (fpw_lsn != InvalidXLogRecPtr && fpw_lsn <= RedoRecPtr)))
{ {
/* /*
* Oops, some buffer now needs to be backed up that the caller * Oops, some buffer now needs to be backed up that the caller didn't
* didn't back up. Start over. * back up. Start over.
*/ */
WALInsertLockRelease(); WALInsertLockRelease();
END_CRIT_SECTION(); END_CRIT_SECTION();
@ -837,28 +837,13 @@ XLogInsertRecord(XLogRecData *rdata,
* Reserve space for the record in the WAL. This also sets the xl_prev * Reserve space for the record in the WAL. This also sets the xl_prev
* pointer. * pointer.
*/ */
ReserveXLogInsertLocation(rechdr->xl_tot_len, &StartPos, &EndPos, if (isLogSwitch)
&rechdr->xl_prev); inserted = ReserveXLogSwitch(&StartPos, &EndPos, &rechdr->xl_prev);
/* Normal records are always inserted. */
inserted = true;
}
else else
{ {
/* ReserveXLogInsertLocation(rechdr->xl_tot_len, &StartPos, &EndPos,
* In order to insert an XLOG_SWITCH record, we need to hold all of &rechdr->xl_prev);
* the WAL insertion locks, not just one, so that no one else can inserted = true;
* begin inserting a record until we've figured out how much space
* remains in the current WAL segment and claimed all of it.
*
* Nonetheless, this case is simpler than the normal cases handled
* above, which must check for changes in doPageWrites and RedoRecPtr.
* Those checks are only needed for records that can contain
* full-pages images, and an XLOG_SWITCH record never does.
*/
Assert(fpw_lsn == InvalidXLogRecPtr);
WALInsertLockAcquireExclusive();
inserted = ReserveXLogSwitch(&StartPos, &EndPos, &rechdr->xl_prev);
} }
if (inserted) if (inserted)
@ -6737,9 +6722,7 @@ CreateCheckPoint(int flags)
{ {
do do
{ {
pgstat_report_wait_start(WAIT_EVENT_CHECKPOINT_DELAY_START);
pg_usleep(10000L); /* wait for 10 msec */ pg_usleep(10000L); /* wait for 10 msec */
pgstat_report_wait_end();
} while (HaveVirtualXIDsDelayingChkpt(vxids, nvxids, } while (HaveVirtualXIDsDelayingChkpt(vxids, nvxids,
DELAY_CHKPT_START)); DELAY_CHKPT_START));
} }
@ -6752,9 +6735,7 @@ CreateCheckPoint(int flags)
{ {
do do
{ {
pgstat_report_wait_start(WAIT_EVENT_CHECKPOINT_DELAY_COMPLETE);
pg_usleep(10000L); /* wait for 10 msec */ pg_usleep(10000L); /* wait for 10 msec */
pgstat_report_wait_end();
} while (HaveVirtualXIDsDelayingChkpt(vxids, nvxids, } while (HaveVirtualXIDsDelayingChkpt(vxids, nvxids,
DELAY_CHKPT_COMPLETE)); DELAY_CHKPT_COMPLETE));
} }

View File

@ -2108,7 +2108,7 @@ InitPartitionPruneContext(PartitionPruneContext *context,
foreach(lc, pruning_steps) foreach(lc, pruning_steps)
{ {
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc); PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
ListCell *lc2 = list_head(step->exprs); ListCell *lc2;
int keyno; int keyno;
/* not needed for other step kinds */ /* not needed for other step kinds */
@ -2117,14 +2117,10 @@ InitPartitionPruneContext(PartitionPruneContext *context,
Assert(list_length(step->exprs) <= partnatts); 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)) Expr *expr = (Expr *) lfirst(lc2);
continue;
if (lc2 != NULL)
{
Expr *expr = lfirst(lc2);
/* not needed for Consts */ /* not needed for Consts */
if (!IsA(expr, Const)) if (!IsA(expr, Const))
@ -2137,8 +2133,8 @@ InitPartitionPruneContext(PartitionPruneContext *context,
* When planstate is NULL, pruning_steps is known not to * When planstate is NULL, pruning_steps is known not to
* contain any expressions that depend on the parent plan. * contain any expressions that depend on the parent plan.
* Information of any available EXTERN parameters must be * Information of any available EXTERN parameters must be
* passed explicitly in that case, which the caller must * passed explicitly in that case, which the caller must have
* have made available via econtext. * made available via econtext.
*/ */
if (planstate == NULL) if (planstate == NULL)
context->exprstates[stateidx] = context->exprstates[stateidx] =
@ -2148,8 +2144,7 @@ InitPartitionPruneContext(PartitionPruneContext *context,
context->exprstates[stateidx] = context->exprstates[stateidx] =
ExecInitExpr(expr, context->planstate); ExecInitExpr(expr, context->planstate);
} }
lc2 = lnext(step->exprs, lc2); keyno++;
}
} }
} }
} }

View File

@ -167,6 +167,7 @@ static List *get_steps_using_prefix(GeneratePruningStepsContext *context,
bool step_op_is_ne, bool step_op_is_ne,
Expr *step_lastexpr, Expr *step_lastexpr,
Oid step_lastcmpfn, Oid step_lastcmpfn,
int step_lastkeyno,
Bitmapset *step_nullkeys, Bitmapset *step_nullkeys,
List *prefix); List *prefix);
static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, 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, bool step_op_is_ne,
Expr *step_lastexpr, Expr *step_lastexpr,
Oid step_lastcmpfn, Oid step_lastcmpfn,
int step_lastkeyno,
Bitmapset *step_nullkeys, Bitmapset *step_nullkeys,
List *prefix, List *prefix,
ListCell *start, ListCell *start,
@ -1529,6 +1531,7 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
pc->op_is_ne, pc->op_is_ne,
pc->expr, pc->expr,
pc->cmpfn, pc->cmpfn,
0,
NULL, NULL,
NIL); NIL);
opsteps = list_concat(opsteps, pc_steps); opsteps = list_concat(opsteps, pc_steps);
@ -1654,6 +1657,7 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
pc->op_is_ne, pc->op_is_ne,
pc->expr, pc->expr,
pc->cmpfn, pc->cmpfn,
pc->keyno,
NULL, NULL,
prefix); prefix);
opsteps = list_concat(opsteps, pc_steps); opsteps = list_concat(opsteps, pc_steps);
@ -1727,6 +1731,7 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
false, false,
pc->expr, pc->expr,
pc->cmpfn, pc->cmpfn,
pc->keyno,
nullkeys, nullkeys,
prefix); prefix);
opsteps = list_concat(opsteps, pc_steps); opsteps = list_concat(opsteps, pc_steps);
@ -2345,31 +2350,25 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
/* /*
* get_steps_using_prefix * 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 * To generate steps, step_lastexpr and step_lastcmpfn are appended to
* belonging to the final partition key that we have a clause for. 'prefix' * expressions and cmpfns, respectively, extracted from the clauses in
* is a list of PartClauseInfos for partition key numbers prior to the given * 'prefix'. Actually, since 'prefix' may contain multiple clauses for the
* 'step_lastexpr' and 'step_lastcmpfn'. 'prefix' may contain multiple * same partition key column, we must generate steps for various combinations
* PartClauseInfos belonging to a single partition key. We will generate a * of the clauses of different keys.
* PartitionPruneStepOp for each combination of the given PartClauseInfos
* using, at most, one PartClauseInfo per partition key.
* *
* For LIST and RANGE partitioned tables, callers must ensure that * For list/range partitioning, callers must ensure that step_nullkeys is
* step_nullkeys is NULL, and that prefix contains at least one clause for * NULL, and that prefix contains at least one clause for each of the
* each of the partition keys prior to the key that 'step_lastexpr' and * partition keys earlier than one specified in step_lastkeyno if it's
* 'step_lastcmpfn'belong to. * 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 * For both cases, callers must also ensure that clauses in prefix are sorted
* least one clause for each of the partition keys apart from the final key * in ascending order of their partition key numbers.
* (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.
*/ */
static List * static List *
get_steps_using_prefix(GeneratePruningStepsContext *context, get_steps_using_prefix(GeneratePruningStepsContext *context,
@ -2377,17 +2376,14 @@ get_steps_using_prefix(GeneratePruningStepsContext *context,
bool step_op_is_ne, bool step_op_is_ne,
Expr *step_lastexpr, Expr *step_lastexpr,
Oid step_lastcmpfn, Oid step_lastcmpfn,
int step_lastkeyno,
Bitmapset *step_nullkeys, Bitmapset *step_nullkeys,
List *prefix) List *prefix)
{ {
/* step_nullkeys must be empty for RANGE and LIST partitioned tables */
Assert(step_nullkeys == NULL || Assert(step_nullkeys == NULL ||
context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH); context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
/* /* Quick exit if there are no values to prefix with. */
* No recursive processing is required when 'prefix' is an empty list.
* This occurs when there is only 1 partition key column.
*/
if (prefix == NIL) if (prefix == NIL)
{ {
PartitionPruneStep *step; PartitionPruneStep *step;
@ -2401,12 +2397,13 @@ get_steps_using_prefix(GeneratePruningStepsContext *context,
return list_make1(step); 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, return get_steps_using_prefix_recurse(context,
step_opstrategy, step_opstrategy,
step_op_is_ne, step_op_is_ne,
step_lastexpr, step_lastexpr,
step_lastcmpfn, step_lastcmpfn,
step_lastkeyno,
step_nullkeys, step_nullkeys,
prefix, prefix,
list_head(prefix), list_head(prefix),
@ -2415,17 +2412,13 @@ get_steps_using_prefix(GeneratePruningStepsContext *context,
/* /*
* get_steps_using_prefix_recurse * get_steps_using_prefix_recurse
* Generate and return a list of PartitionPruneStepOps using the 'prefix' * Recursively generate combinations of clauses for different partition
* list of PartClauseInfos starting at the 'start' cell. * 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 * 'prefix' is the list of PartClauseInfos.
* we create a PartitionPruneStepOp for each combination of duplicated * 'start' is where we should start iterating for the current invocation.
* 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.
* 'step_exprs' and 'step_cmpfns' each contains the expressions and cmpfns * 'step_exprs' and 'step_cmpfns' each contains the expressions and cmpfns
* we've generated so far from the clauses for the previous part keys. * we've generated so far from the clauses for the previous part keys.
*/ */
@ -2435,6 +2428,7 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
bool step_op_is_ne, bool step_op_is_ne,
Expr *step_lastexpr, Expr *step_lastexpr,
Oid step_lastcmpfn, Oid step_lastcmpfn,
int step_lastkeyno,
Bitmapset *step_nullkeys, Bitmapset *step_nullkeys,
List *prefix, List *prefix,
ListCell *start, ListCell *start,
@ -2444,25 +2438,23 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
List *result = NIL; List *result = NIL;
ListCell *lc; ListCell *lc;
int cur_keyno; int cur_keyno;
int final_keyno;
/* Actually, recursion would be limited by PARTITION_MAX_KEYS. */ /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */
check_stack_depth(); check_stack_depth();
/* Check if we need to recurse. */
Assert(start != NULL); Assert(start != NULL);
cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno; cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno;
final_keyno = ((PartClauseInfo *) llast(prefix))->keyno; if (cur_keyno < step_lastkeyno - 1)
/* Check if we need to recurse. */
if (cur_keyno < final_keyno)
{ {
PartClauseInfo *pc; PartClauseInfo *pc;
ListCell *next_start; ListCell *next_start;
/* /*
* Find the first PartClauseInfo belonging to the next partition key, * For each clause with cur_keyno, add its expr and cmpfn to
* the next recursive call must start iteration of the prefix list * step_exprs and step_cmpfns, respectively, and recurse after setting
* from that point. * next_start to the ListCell of the first clause for the next
* partition key.
*/ */
for_each_cell(lc, prefix, start) for_each_cell(lc, prefix, start)
{ {
@ -2471,15 +2463,8 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
if (pc->keyno > cur_keyno) if (pc->keyno > cur_keyno)
break; break;
} }
/* record where to start iterating in the next recursive call */
next_start = lc; 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) for_each_cell(lc, prefix, start)
{ {
List *moresteps; List *moresteps;
@ -2499,7 +2484,6 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
} }
else else
{ {
/* check the 'prefix' list is sorted correctly */
Assert(pc->keyno > cur_keyno); Assert(pc->keyno > cur_keyno);
break; break;
} }
@ -2509,6 +2493,7 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
step_op_is_ne, step_op_is_ne,
step_lastexpr, step_lastexpr,
step_lastcmpfn, step_lastcmpfn,
step_lastkeyno,
step_nullkeys, step_nullkeys,
prefix, prefix,
next_start, next_start,
@ -2527,8 +2512,8 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
* each clause with cur_keyno, which is all clauses from here onward * each clause with cur_keyno, which is all clauses from here onward
* till the end of the list. Note that for hash partitioning, * till the end of the list. Note that for hash partitioning,
* step_nullkeys is allowed to be non-empty, in which case step_exprs * step_nullkeys is allowed to be non-empty, in which case step_exprs
* would only contain expressions for the partition keys that are not * would only contain expressions for the earlier partition keys that
* specified in step_nullkeys. * are not specified in step_nullkeys.
*/ */
Assert(list_length(step_exprs) == cur_keyno || Assert(list_length(step_exprs) == cur_keyno ||
!bms_is_empty(step_nullkeys)); !bms_is_empty(step_nullkeys));

View File

@ -97,8 +97,6 @@ BGWORKER_SHUTDOWN "Waiting for background worker to shut down."
BGWORKER_STARTUP "Waiting for background worker to start up." BGWORKER_STARTUP "Waiting for background worker to start up."
BTREE_PAGE "Waiting for the page number needed to continue a parallel B-tree scan to become available." BTREE_PAGE "Waiting for the page number needed to continue a parallel B-tree scan to become available."
BUFFER_IO "Waiting for buffer I/O to complete." BUFFER_IO "Waiting for buffer I/O to complete."
CHECKPOINT_DELAY_COMPLETE "Waiting for a backend that blocks a checkpoint from completing."
CHECKPOINT_DELAY_START "Waiting for a backend that blocks a checkpoint from starting."
CHECKPOINT_DONE "Waiting for a checkpoint to complete." CHECKPOINT_DONE "Waiting for a checkpoint to complete."
CHECKPOINT_START "Waiting for a checkpoint to start." CHECKPOINT_START "Waiting for a checkpoint to start."
EXECUTE_GATHER "Waiting for activity from a child process while executing a <literal>Gather</literal> plan node." EXECUTE_GATHER "Waiting for activity from a child process while executing a <literal>Gather</literal> plan node."

View File

@ -1948,6 +1948,7 @@ explain (costs off) select * from hp where a = 1 and b = 'abcde' and
One-Time Filter: false One-Time Filter: false
(2 rows) (2 rows)
drop table hp;
-- --
-- Test runtime partition pruning -- 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)) Filter: ((b >= $1) AND (b <= $2) AND (a < $0))
(10 rows) (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 -- Test a backwards Append scan
create table list_part (a int) partition by list (a); create table list_part (a int) partition by list (a);
create table list_part1 partition of list_part for values in (1); create table list_part1 partition of list_part for values in (1);
@ -4031,217 +4011,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)) Filter: ((a >= 1) AND (b >= 1) AND (d >= 0) AND (b = 2) AND (c = 2))
(2 rows) (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_test1;
drop table rp_prefix_test2; drop table rp_prefix_test2;
drop table rp_prefix_test3; 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; drop table hp_prefix_test;
-- --
-- Check that gen_partprune_steps() detects self-contradiction from clauses -- 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 explain (costs off) select * from hp where a = 1 and b = 'abcde' and
(c = 2 or c = 3); (c = 2 or c = 3);
drop table hp;
-- --
-- Test runtime partition pruning -- 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); 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 -- Test a backwards Append scan
create table list_part (a int) partition by list (a); create table list_part (a int) partition by list (a);
create table list_part1 partition of list_part for values in (1); 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) -- 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; 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_test1;
drop table rp_prefix_test2; drop table rp_prefix_test2;
drop table rp_prefix_test3; 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; drop table hp_prefix_test;
-- --