mirror of
https://github.com/postgres/postgres.git
synced 2025-07-20 00:01:34 -04:00
Compare commits
6 Commits
e7689190b3
...
0013ba290b
Author | SHA1 | Date | |
---|---|---|---|
|
0013ba290b | ||
|
7e1f544827 | ||
|
df9a3d4e99 | ||
|
d9e46dfb78 | ||
|
dab5538f0b | ||
|
f0c409d9c7 |
@ -5271,13 +5271,14 @@ 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 a partitioned
|
or aggregation, which allows grouping or aggregation on partitioned
|
||||||
tables performed separately for each partition. If the <literal>GROUP
|
tables to be performed separately for each partition. If the
|
||||||
BY</literal> clause does not include the partition keys, only partial
|
<literal>GROUP BY</literal> clause does not include the partition
|
||||||
aggregation can be performed on a per-partition basis, and
|
keys, only partial aggregation can be performed on a per-partition
|
||||||
finalization must be performed later. Because partitionwise grouping
|
basis, and finalization must be performed later. Because
|
||||||
or aggregation can use significantly more CPU time and memory during
|
partitionwise grouping or aggregation can use significantly more CPU
|
||||||
planning, the default is <literal>off</literal>.
|
time and memory during planning, the default is
|
||||||
|
<literal>off</literal>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -10738,6 +10738,13 @@ 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">
|
||||||
|
@ -792,59 +792,74 @@ XLogInsertRecord(XLogRecData *rdata,
|
|||||||
*----------
|
*----------
|
||||||
*/
|
*/
|
||||||
START_CRIT_SECTION();
|
START_CRIT_SECTION();
|
||||||
if (isLogSwitch)
|
|
||||||
WALInsertLockAcquireExclusive();
|
if (likely(!isLogSwitch))
|
||||||
else
|
{
|
||||||
WALInsertLockAcquire();
|
WALInsertLockAcquire();
|
||||||
|
|
||||||
/*
|
|
||||||
* Check to see if my copy of RedoRecPtr is out of date. If so, may have
|
|
||||||
* to go back and have the caller recompute everything. This can only
|
|
||||||
* happen just after a checkpoint, so it's better to be slow in this case
|
|
||||||
* and fast otherwise.
|
|
||||||
*
|
|
||||||
* 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 already
|
|
||||||
* doing full-page writes then go back and recompute.
|
|
||||||
*
|
|
||||||
* If we aren't doing full-page writes then RedoRecPtr doesn't actually
|
|
||||||
* affect the contents of the XLOG record, so we'll update our local copy
|
|
||||||
* but not force a recomputation. (If doPageWrites was just turned off,
|
|
||||||
* we could recompute the record without full pages, but we choose not to
|
|
||||||
* bother.)
|
|
||||||
*/
|
|
||||||
if (RedoRecPtr != Insert->RedoRecPtr)
|
|
||||||
{
|
|
||||||
Assert(RedoRecPtr < Insert->RedoRecPtr);
|
|
||||||
RedoRecPtr = Insert->RedoRecPtr;
|
|
||||||
}
|
|
||||||
doPageWrites = (Insert->fullPageWrites || Insert->runningBackups > 0);
|
|
||||||
|
|
||||||
if (doPageWrites &&
|
|
||||||
(!prevDoPageWrites ||
|
|
||||||
(fpw_lsn != InvalidXLogRecPtr && fpw_lsn <= RedoRecPtr)))
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
* Oops, some buffer now needs to be backed up that the caller didn't
|
* Check to see if my copy of RedoRecPtr is out of date. If so, may
|
||||||
* back up. Start over.
|
* have to go back and have the caller recompute everything. This can
|
||||||
|
* only happen just after a checkpoint, so it's better to be slow in
|
||||||
|
* this case and fast otherwise.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* already doing full-page writes then go back and recompute.
|
||||||
|
*
|
||||||
|
* If we aren't doing full-page writes then RedoRecPtr doesn't
|
||||||
|
* actually affect the contents of the XLOG record, so we'll update
|
||||||
|
* our local copy but not force a recomputation. (If doPageWrites was
|
||||||
|
* just turned off, we could recompute the record without full pages,
|
||||||
|
* but we choose not to bother.)
|
||||||
*/
|
*/
|
||||||
WALInsertLockRelease();
|
if (RedoRecPtr != Insert->RedoRecPtr)
|
||||||
END_CRIT_SECTION();
|
{
|
||||||
return InvalidXLogRecPtr;
|
Assert(RedoRecPtr < Insert->RedoRecPtr);
|
||||||
}
|
RedoRecPtr = Insert->RedoRecPtr;
|
||||||
|
}
|
||||||
|
doPageWrites = (Insert->fullPageWrites || Insert->runningBackups > 0);
|
||||||
|
|
||||||
/*
|
if (doPageWrites &&
|
||||||
* Reserve space for the record in the WAL. This also sets the xl_prev
|
(!prevDoPageWrites ||
|
||||||
* pointer.
|
(fpw_lsn != InvalidXLogRecPtr && fpw_lsn <= RedoRecPtr)))
|
||||||
*/
|
{
|
||||||
if (isLogSwitch)
|
/*
|
||||||
inserted = ReserveXLogSwitch(&StartPos, &EndPos, &rechdr->xl_prev);
|
* Oops, some buffer now needs to be backed up that the caller
|
||||||
else
|
* didn't back up. Start over.
|
||||||
{
|
*/
|
||||||
|
WALInsertLockRelease();
|
||||||
|
END_CRIT_SECTION();
|
||||||
|
return InvalidXLogRecPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reserve space for the record in the WAL. This also sets the xl_prev
|
||||||
|
* pointer.
|
||||||
|
*/
|
||||||
ReserveXLogInsertLocation(rechdr->xl_tot_len, &StartPos, &EndPos,
|
ReserveXLogInsertLocation(rechdr->xl_tot_len, &StartPos, &EndPos,
|
||||||
&rechdr->xl_prev);
|
&rechdr->xl_prev);
|
||||||
|
|
||||||
|
/* Normal records are always inserted. */
|
||||||
inserted = true;
|
inserted = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* In order to insert an XLOG_SWITCH record, we need to hold all of
|
||||||
|
* the WAL insertion locks, not just one, so that no one else can
|
||||||
|
* 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)
|
||||||
{
|
{
|
||||||
@ -6722,7 +6737,9 @@ 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));
|
||||||
}
|
}
|
||||||
@ -6735,7 +6752,9 @@ 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));
|
||||||
}
|
}
|
||||||
|
@ -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;
|
ListCell *lc2 = list_head(step->exprs);
|
||||||
int keyno;
|
int keyno;
|
||||||
|
|
||||||
/* not needed for other step kinds */
|
/* not needed for other step kinds */
|
||||||
@ -2117,34 +2117,39 @@ InitPartitionPruneContext(PartitionPruneContext *context,
|
|||||||
|
|
||||||
Assert(list_length(step->exprs) <= partnatts);
|
Assert(list_length(step->exprs) <= partnatts);
|
||||||
|
|
||||||
keyno = 0;
|
for (keyno = 0; keyno < partnatts; keyno++)
|
||||||
foreach(lc2, step->exprs)
|
|
||||||
{
|
{
|
||||||
Expr *expr = (Expr *) lfirst(lc2);
|
if (bms_is_member(keyno, step->nullkeys))
|
||||||
|
continue;
|
||||||
|
|
||||||
/* not needed for Consts */
|
if (lc2 != NULL)
|
||||||
if (!IsA(expr, Const))
|
|
||||||
{
|
{
|
||||||
int stateidx = PruneCxtStateIdx(partnatts,
|
Expr *expr = lfirst(lc2);
|
||||||
step->step.step_id,
|
|
||||||
keyno);
|
|
||||||
|
|
||||||
/*
|
/* not needed for Consts */
|
||||||
* When planstate is NULL, pruning_steps is known not to
|
if (!IsA(expr, Const))
|
||||||
* contain any expressions that depend on the parent plan.
|
{
|
||||||
* Information of any available EXTERN parameters must be
|
int stateidx = PruneCxtStateIdx(partnatts,
|
||||||
* passed explicitly in that case, which the caller must have
|
step->step.step_id,
|
||||||
* made available via econtext.
|
keyno);
|
||||||
*/
|
|
||||||
if (planstate == NULL)
|
/*
|
||||||
context->exprstates[stateidx] =
|
* When planstate is NULL, pruning_steps is known not to
|
||||||
ExecInitExprWithParams(expr,
|
* contain any expressions that depend on the parent plan.
|
||||||
econtext->ecxt_param_list_info);
|
* Information of any available EXTERN parameters must be
|
||||||
else
|
* passed explicitly in that case, which the caller must
|
||||||
context->exprstates[stateidx] =
|
* have made available via econtext.
|
||||||
ExecInitExpr(expr, context->planstate);
|
*/
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
keyno++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,6 @@ 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,
|
||||||
@ -175,7 +174,6 @@ 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,
|
||||||
@ -1531,7 +1529,6 @@ 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);
|
||||||
@ -1657,7 +1654,6 @@ 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);
|
||||||
@ -1731,7 +1727,6 @@ 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);
|
||||||
@ -2350,25 +2345,31 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* get_steps_using_prefix
|
* get_steps_using_prefix
|
||||||
* Generate list of PartitionPruneStepOp steps each consisting of given
|
* Generate a list of PartitionPruneStepOps based on the given input.
|
||||||
* opstrategy
|
|
||||||
*
|
*
|
||||||
* To generate steps, step_lastexpr and step_lastcmpfn are appended to
|
* 'step_lastexpr' and 'step_lastcmpfn' are the Expr and comparison function
|
||||||
* expressions and cmpfns, respectively, extracted from the clauses in
|
* belonging to the final partition key that we have a clause for. 'prefix'
|
||||||
* 'prefix'. Actually, since 'prefix' may contain multiple clauses for the
|
* is a list of PartClauseInfos for partition key numbers prior to the given
|
||||||
* same partition key column, we must generate steps for various combinations
|
* 'step_lastexpr' and 'step_lastcmpfn'. 'prefix' may contain multiple
|
||||||
* of the clauses of different keys.
|
* 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.
|
||||||
*
|
*
|
||||||
* For list/range partitioning, callers must ensure that step_nullkeys is
|
* For LIST and RANGE partitioned tables, callers must ensure that
|
||||||
* NULL, and that prefix contains at least one clause for each of the
|
* step_nullkeys is NULL, and that prefix contains at least one clause for
|
||||||
* partition keys earlier than one specified in step_lastkeyno if it's
|
* each of the partition keys prior to the key that 'step_lastexpr' and
|
||||||
* greater than zero. For hash partitioning, step_nullkeys is allowed to be
|
* 'step_lastcmpfn'belong to.
|
||||||
* 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
|
* For HASH partitioned tables, callers must ensure that 'prefix' contains at
|
||||||
* in ascending order of their partition key numbers.
|
* 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.
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
get_steps_using_prefix(GeneratePruningStepsContext *context,
|
get_steps_using_prefix(GeneratePruningStepsContext *context,
|
||||||
@ -2376,14 +2377,17 @@ 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;
|
||||||
@ -2397,13 +2401,12 @@ get_steps_using_prefix(GeneratePruningStepsContext *context,
|
|||||||
return list_make1(step);
|
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,
|
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),
|
||||||
@ -2412,13 +2415,17 @@ get_steps_using_prefix(GeneratePruningStepsContext *context,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* get_steps_using_prefix_recurse
|
* get_steps_using_prefix_recurse
|
||||||
* Recursively generate combinations of clauses for different partition
|
* Generate and return a list of PartitionPruneStepOps using the 'prefix'
|
||||||
* keys and start generating steps upon reaching clauses for the greatest
|
* list of PartClauseInfos starting at the 'start' cell.
|
||||||
* column that is less than the one for which we're currently generating
|
|
||||||
* steps (that is, step_lastkeyno)
|
|
||||||
*
|
*
|
||||||
* 'prefix' is the list of PartClauseInfos.
|
* When 'prefix' contains multiple PartClauseInfos for a single partition key
|
||||||
* 'start' is where we should start iterating for the current invocation.
|
* 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.
|
||||||
* '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.
|
||||||
*/
|
*/
|
||||||
@ -2428,7 +2435,6 @@ 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,
|
||||||
@ -2438,23 +2444,25 @@ 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;
|
||||||
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;
|
PartClauseInfo *pc;
|
||||||
ListCell *next_start;
|
ListCell *next_start;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For each clause with cur_keyno, add its expr and cmpfn to
|
* Find the first PartClauseInfo belonging to the next partition key,
|
||||||
* step_exprs and step_cmpfns, respectively, and recurse after setting
|
* the next recursive call must start iteration of the prefix list
|
||||||
* next_start to the ListCell of the first clause for the next
|
* from that point.
|
||||||
* partition key.
|
|
||||||
*/
|
*/
|
||||||
for_each_cell(lc, prefix, start)
|
for_each_cell(lc, prefix, start)
|
||||||
{
|
{
|
||||||
@ -2463,8 +2471,15 @@ 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;
|
||||||
@ -2484,6 +2499,7 @@ 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;
|
||||||
}
|
}
|
||||||
@ -2493,7 +2509,6 @@ 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,
|
||||||
@ -2512,8 +2527,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 earlier partition keys that
|
* would only contain expressions for the partition keys that are not
|
||||||
* are not specified in step_nullkeys.
|
* 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));
|
||||||
|
@ -97,6 +97,8 @@ 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."
|
||||||
|
@ -1948,7 +1948,6 @@ 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
|
||||||
--
|
--
|
||||||
@ -2070,6 +2069,27 @@ 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);
|
||||||
@ -4011,20 +4031,217 @@ 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
|
||||||
|
@ -384,8 +384,6 @@ 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
|
||||||
--
|
--
|
||||||
@ -436,6 +434,25 @@ 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);
|
||||||
@ -1182,16 +1199,57 @@ 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;
|
||||||
|
|
||||||
--
|
--
|
||||||
|
Loading…
x
Reference in New Issue
Block a user