Skip to content

Commit 592d5d7

Browse files
committed
Restructure creation of run-time pruning steps.
Previously, gen_partprune_steps() always built executor pruning steps using all suitable clauses, including those containing PARAM_EXEC Params. This meant that the pruning steps were only completely safe for executor run-time (scan start) pruning. To prune at executor startup, we had to ignore the steps involving exec Params. But this doesn't really work in general, since there may be logic changes needed as well --- for example, pruning according to the last operator's btree strategy is the wrong thing if we're not applying that operator. The rules embodied in gen_partprune_steps() and its minions are sufficiently complicated that tracking their incremental effects in other logic seems quite impractical. Short of a complete redesign, the only safe fix seems to be to run gen_partprune_steps() twice, once to create executor startup pruning steps and then again for run-time pruning steps. We can save a few cycles however by noting during the first scan whether we rejected any clauses because they involved exec Params --- if not, we don't need to do the second scan. In support of this, refactor the internal APIs in partprune.c to make more use of passing information in the GeneratePruningStepsContext struct, rather than as separate arguments. This is, I hope, the last piece of our response to a bug report from Alan Jackson. Back-patch to v11 where this code came in. Discussion: https://postgr.es/m/FAD28A83-AC73-489E-A058-2681FA31D648@tvsquared.com
1 parent 51948c4 commit 592d5d7

File tree

10 files changed

+627
-466
lines changed

10 files changed

+627
-466
lines changed

src/backend/executor/execPartition.c

Lines changed: 121 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
7878
bool *isnull,
7979
int maxfieldlen);
8080
static List *adjust_partition_tlist(List *tlist, TupleConversionMap *map);
81+
static void ExecInitPruningContext(PartitionPruneContext *context,
82+
Oid reloid,
83+
List *pruning_steps,
84+
PlanState *planstate);
8185
static void find_matching_subplans_recurse(PartitionPruningData *prunedata,
8286
PartitionedRelPruningData *pprune,
8387
bool initial_prune,
@@ -1525,18 +1529,13 @@ ExecCreatePartitionPruneState(PlanState *planstate,
15251529
{
15261530
PartitionedRelPruneInfo *pinfo = lfirst_node(PartitionedRelPruneInfo, lc2);
15271531
PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
1528-
PartitionPruneContext *context = &pprune->context;
1529-
PartitionDesc partdesc;
1530-
PartitionKey partkey;
1531-
int partnatts;
1532-
int n_steps;
1533-
ListCell *lc3;
15341532

15351533
/*
15361534
* We must copy the subplan_map rather than pointing directly to
15371535
* the plan's version, as we may end up making modifications to it
15381536
* later.
15391537
*/
1538+
pprune->nparts = pinfo->nparts;
15401539
pprune->subplan_map = palloc(sizeof(int) * pinfo->nparts);
15411540
memcpy(pprune->subplan_map, pinfo->subplan_map,
15421541
sizeof(int) * pinfo->nparts);
@@ -1548,76 +1547,28 @@ ExecCreatePartitionPruneState(PlanState *planstate,
15481547
pprune->present_parts = bms_copy(pinfo->present_parts);
15491548

15501549
/*
1551-
* We need to hold a pin on the partitioned table's relcache entry
1552-
* so that we can rely on its copies of the table's partition key
1553-
* and partition descriptor. We need not get a lock though; one
1554-
* should have been acquired already by InitPlan or
1555-
* ExecLockNonLeafAppendTables.
1550+
* Initialize pruning contexts as needed.
15561551
*/
1557-
context->partrel = relation_open(pinfo->reloid, NoLock);
1558-
1559-
partkey = RelationGetPartitionKey(context->partrel);
1560-
partdesc = RelationGetPartitionDesc(context->partrel);
1561-
n_steps = list_length(pinfo->pruning_steps);
1562-
1563-
context->strategy = partkey->strategy;
1564-
context->partnatts = partnatts = partkey->partnatts;
1565-
context->nparts = pinfo->nparts;
1566-
context->boundinfo = partdesc->boundinfo;
1567-
context->partcollation = partkey->partcollation;
1568-
context->partsupfunc = partkey->partsupfunc;
1569-
1570-
/* We'll look up type-specific support functions as needed */
1571-
context->stepcmpfuncs = (FmgrInfo *)
1572-
palloc0(sizeof(FmgrInfo) * n_steps * partnatts);
1573-
1574-
context->ppccontext = CurrentMemoryContext;
1575-
context->planstate = planstate;
1576-
1577-
/* Initialize expression state for each expression we need */
1578-
context->exprstates = (ExprState **)
1579-
palloc0(sizeof(ExprState *) * n_steps * partnatts);
1580-
foreach(lc3, pinfo->pruning_steps)
1552+
pprune->initial_pruning_steps = pinfo->initial_pruning_steps;
1553+
if (pinfo->initial_pruning_steps)
15811554
{
1582-
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc3);
1583-
ListCell *lc4;
1584-
int keyno;
1585-
1586-
/* not needed for other step kinds */
1587-
if (!IsA(step, PartitionPruneStepOp))
1588-
continue;
1589-
1590-
Assert(list_length(step->exprs) <= partnatts);
1591-
1592-
keyno = 0;
1593-
foreach(lc4, step->exprs)
1594-
{
1595-
Expr *expr = (Expr *) lfirst(lc4);
1596-
1597-
/* not needed for Consts */
1598-
if (!IsA(expr, Const))
1599-
{
1600-
int stateidx = PruneCxtStateIdx(partnatts,
1601-
step->step.step_id,
1602-
keyno);
1603-
1604-
context->exprstates[stateidx] =
1605-
ExecInitExpr(expr, context->planstate);
1606-
}
1607-
keyno++;
1608-
}
1555+
ExecInitPruningContext(&pprune->initial_context,
1556+
pinfo->reloid,
1557+
pinfo->initial_pruning_steps,
1558+
planstate);
1559+
/* Record whether initial pruning is needed at any level */
1560+
prunestate->do_initial_prune = true;
1561+
}
1562+
pprune->exec_pruning_steps = pinfo->exec_pruning_steps;
1563+
if (pinfo->exec_pruning_steps)
1564+
{
1565+
ExecInitPruningContext(&pprune->exec_context,
1566+
pinfo->reloid,
1567+
pinfo->exec_pruning_steps,
1568+
planstate);
1569+
/* Record whether exec pruning is needed at any level */
1570+
prunestate->do_exec_prune = true;
16091571
}
1610-
1611-
/* Array is not modified at runtime, so just point to plan's copy */
1612-
context->exprhasexecparam = pinfo->hasexecparam;
1613-
1614-
pprune->pruning_steps = pinfo->pruning_steps;
1615-
pprune->do_initial_prune = pinfo->do_initial_prune;
1616-
pprune->do_exec_prune = pinfo->do_exec_prune;
1617-
1618-
/* Record if pruning would be useful at any level */
1619-
prunestate->do_initial_prune |= pinfo->do_initial_prune;
1620-
prunestate->do_exec_prune |= pinfo->do_exec_prune;
16211572

16221573
/*
16231574
* Accumulate the IDs of all PARAM_EXEC Params affecting the
@@ -1654,7 +1605,90 @@ ExecDestroyPartitionPruneState(PartitionPruneState *prunestate)
16541605
int j;
16551606

16561607
for (j = 0; j < prunedata->num_partrelprunedata; j++)
1657-
relation_close(pprune[j].context.partrel, NoLock);
1608+
{
1609+
if (pprune[j].initial_pruning_steps)
1610+
relation_close(pprune[j].initial_context.partrel, NoLock);
1611+
if (pprune[j].exec_pruning_steps)
1612+
relation_close(pprune[j].exec_context.partrel, NoLock);
1613+
}
1614+
}
1615+
}
1616+
1617+
/*
1618+
* Initialize a PartitionPruneContext for the given list of pruning steps.
1619+
*/
1620+
static void
1621+
ExecInitPruningContext(PartitionPruneContext *context,
1622+
Oid reloid,
1623+
List *pruning_steps,
1624+
PlanState *planstate)
1625+
{
1626+
PartitionKey partkey;
1627+
PartitionDesc partdesc;
1628+
int n_steps;
1629+
int partnatts;
1630+
ListCell *lc;
1631+
1632+
/*
1633+
* We need to hold a pin on the partitioned table's relcache entry
1634+
* so that we can rely on its copies of the table's partition key
1635+
* and partition descriptor. We need not get a lock though; one
1636+
* should have been acquired already by InitPlan or
1637+
* ExecLockNonLeafAppendTables.
1638+
*/
1639+
context->partrel = relation_open(reloid, NoLock);
1640+
1641+
partkey = RelationGetPartitionKey(context->partrel);
1642+
partdesc = RelationGetPartitionDesc(context->partrel);
1643+
1644+
n_steps = list_length(pruning_steps);
1645+
1646+
context->strategy = partkey->strategy;
1647+
context->partnatts = partnatts = partkey->partnatts;
1648+
context->nparts = partdesc->nparts;
1649+
context->boundinfo = partdesc->boundinfo;
1650+
context->partcollation = partkey->partcollation;
1651+
context->partsupfunc = partkey->partsupfunc;
1652+
1653+
/* We'll look up type-specific support functions as needed */
1654+
context->stepcmpfuncs = (FmgrInfo *)
1655+
palloc0(sizeof(FmgrInfo) * n_steps * partnatts);
1656+
1657+
context->ppccontext = CurrentMemoryContext;
1658+
context->planstate = planstate;
1659+
1660+
/* Initialize expression state for each expression we need */
1661+
context->exprstates = (ExprState **)
1662+
palloc0(sizeof(ExprState *) * n_steps * partnatts);
1663+
foreach(lc, pruning_steps)
1664+
{
1665+
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
1666+
ListCell *lc2;
1667+
int keyno;
1668+
1669+
/* not needed for other step kinds */
1670+
if (!IsA(step, PartitionPruneStepOp))
1671+
continue;
1672+
1673+
Assert(list_length(step->exprs) <= partnatts);
1674+
1675+
keyno = 0;
1676+
foreach(lc2, step->exprs)
1677+
{
1678+
Expr *expr = (Expr *) lfirst(lc2);
1679+
1680+
/* not needed for Consts */
1681+
if (!IsA(expr, Const))
1682+
{
1683+
int stateidx = PruneCxtStateIdx(partnatts,
1684+
step->step.step_id,
1685+
keyno);
1686+
1687+
context->exprstates[stateidx] =
1688+
ExecInitExpr(expr, context->planstate);
1689+
}
1690+
keyno++;
1691+
}
16581692
}
16591693
}
16601694

@@ -1702,7 +1736,8 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubplans)
17021736
find_matching_subplans_recurse(prunedata, pprune, true, &result);
17031737

17041738
/* Expression eval may have used space in node's ps_ExprContext too */
1705-
ResetExprContext(pprune->context.planstate->ps_ExprContext);
1739+
if (pprune->initial_pruning_steps)
1740+
ResetExprContext(pprune->initial_context.planstate->ps_ExprContext);
17061741
}
17071742

17081743
MemoryContextSwitchTo(oldcontext);
@@ -1769,7 +1804,7 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubplans)
17691804
for (j = prunedata->num_partrelprunedata - 1; j >= 0; j--)
17701805
{
17711806
PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
1772-
int nparts = pprune->context.nparts;
1807+
int nparts = pprune->nparts;
17731808
int k;
17741809

17751810
/* We just rebuild present_parts from scratch */
@@ -1854,7 +1889,8 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate)
18541889
find_matching_subplans_recurse(prunedata, pprune, false, &result);
18551890

18561891
/* Expression eval may have used space in node's ps_ExprContext too */
1857-
ResetExprContext(pprune->context.planstate->ps_ExprContext);
1892+
if (pprune->exec_pruning_steps)
1893+
ResetExprContext(pprune->exec_context.planstate->ps_ExprContext);
18581894
}
18591895

18601896
MemoryContextSwitchTo(oldcontext);
@@ -1890,15 +1926,15 @@ find_matching_subplans_recurse(PartitionPruningData *prunedata,
18901926
check_stack_depth();
18911927

18921928
/* Only prune if pruning would be useful at this level. */
1893-
if (initial_prune ? pprune->do_initial_prune : pprune->do_exec_prune)
1929+
if (initial_prune && pprune->initial_pruning_steps)
18941930
{
1895-
PartitionPruneContext *context = &pprune->context;
1896-
1897-
/* Set whether we can evaluate PARAM_EXEC Params or not */
1898-
context->evalexecparams = !initial_prune;
1899-
1900-
partset = get_matching_partitions(context,
1901-
pprune->pruning_steps);
1931+
partset = get_matching_partitions(&pprune->initial_context,
1932+
pprune->initial_pruning_steps);
1933+
}
1934+
else if (!initial_prune && pprune->exec_pruning_steps)
1935+
{
1936+
partset = get_matching_partitions(&pprune->exec_context,
1937+
pprune->exec_pruning_steps);
19021938
}
19031939
else
19041940
{

src/backend/nodes/copyfuncs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,8 @@ _copyPartitionedRelPruneInfo(const PartitionedRelPruneInfo *from)
12031203
COPY_SCALAR_FIELD(do_initial_prune);
12041204
COPY_SCALAR_FIELD(do_exec_prune);
12051205
COPY_BITMAPSET_FIELD(execparamids);
1206+
COPY_NODE_FIELD(initial_pruning_steps);
1207+
COPY_NODE_FIELD(exec_pruning_steps);
12061208

12071209
return newnode;
12081210
}

src/backend/nodes/outfuncs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,8 @@ _outPartitionedRelPruneInfo(StringInfo str, const PartitionedRelPruneInfo *node)
10521052
WRITE_BOOL_FIELD(do_initial_prune);
10531053
WRITE_BOOL_FIELD(do_exec_prune);
10541054
WRITE_BITMAPSET_FIELD(execparamids);
1055+
WRITE_NODE_FIELD(initial_pruning_steps);
1056+
WRITE_NODE_FIELD(exec_pruning_steps);
10551057
}
10561058

10571059
static void

src/backend/nodes/readfuncs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2375,6 +2375,8 @@ _readPartitionedRelPruneInfo(void)
23752375
READ_BOOL_FIELD(do_initial_prune);
23762376
READ_BOOL_FIELD(do_exec_prune);
23772377
READ_BITMAPSET_FIELD(execparamids);
2378+
READ_NODE_FIELD(initial_pruning_steps);
2379+
READ_NODE_FIELD(exec_pruning_steps);
23782380

23792381
READ_DONE();
23802382
}

0 commit comments

Comments
 (0)