wip_improve_runtime_pruning_explain_output.patch
application/octet-stream
Filename: wip_improve_runtime_pruning_explain_output.patch
Type: application/octet-stream
Part: 0
Message:
Re: Runtime pruning problem
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/commands/explain.c | 20 | 15 |
| src/backend/executor/nodeAppend.c | 16 | 33 |
| src/backend/executor/nodeMergeAppend.c | 7 | 24 |
| src/backend/utils/adt/ruleutils.c | 180 | 68 |
| src/include/nodes/execnodes.h | 0 | 3 |
| src/include/utils/ruleutils.h | 3 | 2 |
| src/test/regress/expected/partition_prune.out | 17 | 28 |
| src/test/regress/sql/partition_prune.sql | 1 | 4 |
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a6c6de78f1..cc2b957956 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2075,9 +2075,10 @@ show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
return;
/* Set up deparsing context */
- context = set_deparse_context_planstate(es->deparse_cxt,
- (Node *) planstate,
- ancestors);
+ context = set_deparse_context_plan(es->deparse_cxt,
+ (Node *) planstate,
+ planstate->plan,
+ ancestors);
useprefix = list_length(es->rtable) > 1;
/* Deparse each result column (we now include resjunk ones) */
@@ -2106,9 +2107,10 @@ show_expression(Node *node, const char *qlabel,
char *exprstr;
/* Set up deparsing context */
- context = set_deparse_context_planstate(es->deparse_cxt,
- (Node *) planstate,
- ancestors);
+ context = set_deparse_context_plan(es->deparse_cxt,
+ (Node *) planstate,
+ planstate->plan,
+ ancestors);
/* Deparse the expression */
exprstr = deparse_expression(node, context, useprefix, false);
@@ -2232,9 +2234,10 @@ show_grouping_sets(PlanState *planstate, Agg *agg,
ListCell *lc;
/* Set up deparsing context */
- context = set_deparse_context_planstate(es->deparse_cxt,
- (Node *) planstate,
- ancestors);
+ context = set_deparse_context_plan(es->deparse_cxt,
+ (Node *) planstate,
+ planstate->plan,
+ ancestors);
useprefix = (list_length(es->rtable) > 1 || es->verbose);
ExplainOpenGroup("Grouping Sets", "Grouping Sets", false, es);
@@ -2371,9 +2374,10 @@ show_sort_group_keys(PlanState *planstate, const char *qlabel,
initStringInfo(&sortkeybuf);
/* Set up deparsing context */
- context = set_deparse_context_planstate(es->deparse_cxt,
- (Node *) planstate,
- ancestors);
+ context = set_deparse_context_plan(es->deparse_cxt,
+ (Node *) planstate,
+ planstate->plan,
+ ancestors);
useprefix = (list_length(es->rtable) > 1 || es->verbose);
for (keyno = 0; keyno < nkeys; keyno++)
@@ -2479,9 +2483,10 @@ show_tablesample(TableSampleClause *tsc, PlanState *planstate,
ListCell *lc;
/* Set up deparsing context */
- context = set_deparse_context_planstate(es->deparse_cxt,
- (Node *) planstate,
- ancestors);
+ context = set_deparse_context_plan(es->deparse_cxt,
+ (Node *) planstate,
+ planstate->plan,
+ ancestors);
useprefix = list_length(es->rtable) > 1;
/* Get the tablesample method name */
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index f3be2429db..26a4409fef 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -78,7 +78,6 @@ struct ParallelAppendState
};
#define INVALID_SUBPLAN_INDEX -1
-#define NO_MATCHING_SUBPLANS -2
static TupleTableSlot *ExecAppend(PlanState *pstate);
static bool choose_next_subplan_locally(AppendState *node);
@@ -142,23 +141,6 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
list_length(node->appendplans));
- /*
- * The case where no subplans survive pruning must be handled
- * specially. The problem here is that code in explain.c requires
- * an Append to have at least one subplan in order for it to
- * properly determine the Vars in that subplan's targetlist. We
- * sidestep this issue by just initializing the first subplan and
- * setting as_whichplan to NO_MATCHING_SUBPLANS to indicate that
- * we don't really need to scan any subnodes.
- */
- if (bms_is_empty(validsubplans))
- {
- appendstate->as_whichplan = NO_MATCHING_SUBPLANS;
-
- /* Mark the first as valid so that it's initialized below */
- validsubplans = bms_make_singleton(0);
- }
-
nplans = bms_num_members(validsubplans);
}
else
@@ -170,14 +152,12 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
}
/*
- * If no runtime pruning is required, we can fill as_valid_subplans
- * immediately, preventing later calls to ExecFindMatchingSubPlans.
+ * If there's at least one valid plan and no run-time pruning is
+ * required, we can fill as_valid_subplans immediately, preventing
+ * later calls to ExecFindMatchingSubPlans.
*/
- if (!prunestate->do_exec_prune)
- {
- Assert(nplans > 0);
+ if (nplans > 0 && !prunestate->do_exec_prune)
appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
- }
}
else
{
@@ -202,8 +182,11 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
appendstate->ps.resultopsset = true;
appendstate->ps.resultopsfixed = false;
- appendplanstates = (PlanState **) palloc(nplans *
- sizeof(PlanState *));
+ if (nplans > 0)
+ appendplanstates = (PlanState **) palloc(nplans *
+ sizeof(PlanState *));
+ else
+ appendplanstates = NULL;
/*
* call ExecInitNode on each of the valid plans to be executed and save
@@ -260,6 +243,10 @@ ExecAppend(PlanState *pstate)
if (node->as_whichplan < 0)
{
+ /* Nothing to do if there are no subplans */
+ if (node->as_nplans == 0)
+ return ExecClearTuple(node->ps.ps_ResultTupleSlot);
+
/*
* If no subplan has been chosen, we must choose one before
* proceeding.
@@ -267,10 +254,6 @@ ExecAppend(PlanState *pstate)
if (node->as_whichplan == INVALID_SUBPLAN_INDEX &&
!node->choose_next_subplan(node))
return ExecClearTuple(node->ps.ps_ResultTupleSlot);
-
- /* Nothing to do if there are no matching subplans */
- else if (node->as_whichplan == NO_MATCHING_SUBPLANS)
- return ExecClearTuple(node->ps.ps_ResultTupleSlot);
}
for (;;)
@@ -465,7 +448,7 @@ choose_next_subplan_locally(AppendState *node)
int nextplan;
/* We should never be called when there are no subplans */
- Assert(whichplan != NO_MATCHING_SUBPLANS);
+ Assert(node->as_nplans > 0);
/*
* If first call then have the bms member function choose the first valid
@@ -516,7 +499,7 @@ choose_next_subplan_for_leader(AppendState *node)
Assert(ScanDirectionIsForward(node->ps.state->es_direction));
/* We should never be called when there are no subplans */
- Assert(node->as_whichplan != NO_MATCHING_SUBPLANS);
+ Assert(node->as_nplans > 0);
LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
@@ -597,7 +580,7 @@ choose_next_subplan_for_worker(AppendState *node)
Assert(ScanDirectionIsForward(node->ps.state->es_direction));
/* We should never be called when there are no subplans */
- Assert(node->as_whichplan != NO_MATCHING_SUBPLANS);
+ Assert(node->as_nplans > 0);
LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index 7ba53ba185..c08c4f38ff 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -81,7 +81,6 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
mergestate->ps.plan = (Plan *) node;
mergestate->ps.state = estate;
mergestate->ps.ExecProcNode = ExecMergeAppend;
- mergestate->ms_noopscan = false;
/* If run-time partition pruning is enabled, then set that up now */
if (node->part_prune_info != NULL)
@@ -102,23 +101,6 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
list_length(node->mergeplans));
- /*
- * The case where no subplans survive pruning must be handled
- * specially. The problem here is that code in explain.c requires
- * a MergeAppend to have at least one subplan in order for it to
- * properly determine the Vars in that subplan's targetlist. We
- * sidestep this issue by just initializing the first subplan and
- * setting ms_noopscan to true to indicate that we don't really
- * need to scan any subnodes.
- */
- if (bms_is_empty(validsubplans))
- {
- mergestate->ms_noopscan = true;
-
- /* Mark the first as valid so that it's initialized below */
- validsubplans = bms_make_singleton(0);
- }
-
nplans = bms_num_members(validsubplans);
}
else
@@ -133,11 +115,8 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
* If no runtime pruning is required, we can fill ms_valid_subplans
* immediately, preventing later calls to ExecFindMatchingSubPlans.
*/
- if (!prunestate->do_exec_prune)
- {
- Assert(nplans > 0);
+ if (nplans > 0 && !prunestate->do_exec_prune)
mergestate->ms_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
- }
}
else
{
@@ -153,7 +132,11 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
mergestate->ms_prune_state = NULL;
}
- mergeplanstates = (PlanState **) palloc(nplans * sizeof(PlanState *));
+ if (nplans > 0)
+ mergeplanstates = (PlanState **) palloc(nplans * sizeof(PlanState *));
+ else
+ mergeplanstates = NULL;
+
mergestate->mergeplans = mergeplanstates;
mergestate->ms_nplans = nplans;
@@ -244,7 +227,7 @@ ExecMergeAppend(PlanState *pstate)
if (!node->ms_initialized)
{
/* Nothing to do if all subplans were pruned */
- if (node->ms_noopscan)
+ if (node->ms_nplans == 0)
return ExecClearTuple(node->ps.ps_ResultTupleSlot);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 0c7a533e69..712ff3a9a6 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -146,7 +146,13 @@ typedef struct
* state nodes, as well as their plan nodes' targetlists, and the index tlist
* if the current plan node might contain INDEX_VAR Vars. (These fields could
* be derived on-the-fly from the current PlanState, but it seems notationally
- * clearer to set them up as separate fields.)
+ * clearer to set them up as separate fields.). Additionally we store the Plan
+ * nodes corisponding to each PlanState node. It might appear like we could
+ * just obtain these from looking at the PlanStates->plan field, but in some
+ * cases a PlanState can be NULL when a Plan node was not initialized during
+ * executor startup. This can happen for things such as run-time partition
+ * pruning. Storing these allows us to still resolve Vars for Plan nodes that
+ * were never initialized.
*/
typedef struct
{
@@ -158,6 +164,10 @@ typedef struct
bool unique_using; /* Are we making USING names globally unique */
List *using_names; /* List of assigned names for USING columns */
/* Remaining fields are used only when deparsing a Plan tree: */
+ Plan *plan; /* immediate parent of current expression */
+ Plan *outer_plan; /* outer subplan, or NULL if none */
+ Plan *inner_plan; /* inner subplan, or NULL if none */
+ List *ancestor_plans; /* ancestors of plan */
PlanState *planstate; /* immediate parent of current expression */
List *ancestors; /* ancestors of planstate */
PlanState *outer_planstate; /* outer subplan state, or NULL if none */
@@ -358,12 +368,14 @@ static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
static void flatten_join_using_qual(Node *qual,
List **leftvars, List **rightvars);
static char *get_rtable_name(int rtindex, deparse_context *context);
-static void set_deparse_planstate(deparse_namespace *dpns, PlanState *ps);
+static void set_deparse_plan(deparse_namespace *dpns, PlanState *ps,
+ Plan *plan);
static void push_child_plan(deparse_namespace *dpns, PlanState *ps,
- deparse_namespace *save_dpns);
+ Plan *plan, deparse_namespace *save_dpns);
static void pop_child_plan(deparse_namespace *dpns,
deparse_namespace *save_dpns);
static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
+ ListCell *ancestor_plan_cell,
deparse_namespace *save_dpns);
static void pop_ancestor_plan(deparse_namespace *dpns,
deparse_namespace *save_dpns);
@@ -410,7 +422,8 @@ static void resolve_special_varno(Node *node, deparse_context *context,
void *private,
void (*callback) (Node *, deparse_context *, void *));
static Node *find_param_referent(Param *param, deparse_context *context,
- deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
+ deparse_namespace **dpns_p, ListCell **ancestor_cell_p,
+ ListCell **ancestor_plan_cell_p);
static void get_parameter(Param *param, deparse_context *context);
static const char *get_simple_binary_op_name(OpExpr *expr);
static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
@@ -3285,7 +3298,7 @@ deparse_context_for(const char *aliasname, Oid relid)
* this is rather expensive if the rangetable is large, and it'll be the same
* for every expression in the Plan tree; so we do it just once and re-use
* the result of this function for each expression. (Note that the result
- * is not usable until set_deparse_context_planstate() is applied to it.)
+ * is not usable until set_deparse_context_plan() is applied to it.)
*
* In addition to the plan's rangetable list, pass the per-RTE alias names
* assigned by a previous call to select_rtable_names_for_explain.
@@ -3314,7 +3327,7 @@ deparse_context_for_plan_rtable(List *rtable, List *rtable_names)
}
/*
- * set_deparse_context_planstate - Specify Plan node containing expression
+ * set_deparse_context_plan - Specify Plan node containing expression
*
* When deparsing an expression in a Plan tree, we might have to resolve
* OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
@@ -3342,19 +3355,32 @@ deparse_context_for_plan_rtable(List *rtable, List *rtable_names)
* The result is the same List passed in; this is a notational convenience.
*/
List *
-set_deparse_context_planstate(List *dpcontext,
- Node *planstate, List *ancestors)
+set_deparse_context_plan(List *dpcontext,
+ Node *planstate, Plan *plan, List *ancestors)
{
deparse_namespace *dpns;
+ ListCell *lc;
/* Should always have one-entry namespace list for Plan deparsing */
Assert(list_length(dpcontext) == 1);
dpns = (deparse_namespace *) linitial(dpcontext);
/* Set our attention on the specific plan node passed in */
- set_deparse_planstate(dpns, (PlanState *) planstate);
+ set_deparse_plan(dpns, (PlanState *) planstate, plan);
dpns->ancestors = ancestors;
+ if (dpns->ancestor_plans != NIL)
+ {
+ list_free(dpns->ancestor_plans);
+ dpns->ancestor_plans = NIL;
+ }
+
+ foreach(lc, ancestors)
+ {
+ PlanState *ps = lfirst(lc);
+ dpns->ancestor_plans = lappend(dpns->ancestor_plans, ps->plan);
+ }
+
return dpcontext;
}
@@ -4620,19 +4646,20 @@ get_rtable_name(int rtindex, deparse_context *context)
}
/*
- * set_deparse_planstate: set up deparse_namespace to parse subexpressions
+ * set_deparse_plan: set up deparse_namespace to parse subexpressions
* of a given PlanState node
*
- * This sets the planstate, outer_planstate, inner_planstate, outer_tlist,
- * inner_tlist, and index_tlist fields. Caller is responsible for adjusting
- * the ancestors list if necessary. Note that the rtable and ctes fields do
- * not need to change when shifting attention to different plan nodes in a
- * single plan tree.
+ * This sets the planstate, outer_planstate, inner_planstate, plan,
+ * outer_plan, inner_plan outer_tlist, inner_tlist, and index_tlist fields.
+ * Caller is responsible for adjusting the ancestors and ancestor_plans lists
+ * if necessary. Note that the rtable and ctes fields do not need to change
+ * when shifting attention to different plan nodes in a single plan tree.
*/
static void
-set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
+set_deparse_plan(deparse_namespace *dpns, PlanState *ps, Plan *plan)
{
dpns->planstate = ps;
+ dpns->plan = plan;
/*
* We special-case Append and MergeAppend to pretend that the first child
@@ -4642,17 +4669,60 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
* first child plan is the OUTER referent; this is to support RETURNING
* lists containing references to non-target relations.
*/
- if (IsA(ps, AppendState))
- dpns->outer_planstate = ((AppendState *) ps)->appendplans[0];
- else if (IsA(ps, MergeAppendState))
- dpns->outer_planstate = ((MergeAppendState *) ps)->mergeplans[0];
- else if (IsA(ps, ModifyTableState))
- dpns->outer_planstate = ((ModifyTableState *) ps)->mt_plans[0];
+ if (IsA(plan, Append))
+ {
+ AppendState *astate = (AppendState *) ps;
+
+ /*
+ * It's possible that run-time partition pruning pruned every subplan
+ * which means we'll have no subnodes in the AppendState. If this
+ * happens we'll use the first of the planned subnodes and set the
+ * outer_planstate to NULL.
+ */
+ if (astate->as_nplans > 0)
+ {
+ dpns->outer_planstate = astate->appendplans[0];
+ dpns->outer_plan = dpns->outer_planstate->plan;
+ }
+ else
+ {
+ dpns->outer_planstate = NULL;
+ dpns->outer_plan = (Plan *) linitial(((Append *) plan)->appendplans);
+ }
+ }
+ else if (IsA(plan, MergeAppend))
+ {
+ MergeAppendState *mastate = (MergeAppendState *) ps;
+
+ /*
+ * Like the Append case above, use the first subplan from the plan if
+ * run-time pruning pruned all of the subnodes.
+ */
+ if (mastate->ms_nplans > 0)
+ {
+ dpns->outer_planstate = mastate->mergeplans[0];
+ dpns->outer_plan = dpns->outer_planstate->plan;
+ }
+ else
+ {
+ dpns->outer_planstate = NULL;
+ dpns->outer_plan = (Plan *) linitial(((MergeAppend *) plan)->mergeplans);
+ }
+ }
+ else if (IsA(plan, ModifyTable))
+ {
+ ModifyTableState *mtstate = (ModifyTableState *) ps;
+ dpns->outer_planstate = mtstate->mt_plans[0];
+ dpns->outer_plan = dpns->outer_planstate->plan;
+ }
else
- dpns->outer_planstate = outerPlanState(ps);
+ {
+ dpns->outer_plan = outerPlan(plan);
+ dpns->outer_planstate = ps != NULL ? outerPlanState(ps) : NULL;
+ }
- if (dpns->outer_planstate)
- dpns->outer_tlist = dpns->outer_planstate->plan->targetlist;
+ if (dpns->outer_plan)
+ dpns->outer_tlist = dpns->outer_plan->targetlist;
else
dpns->outer_tlist = NIL;
@@ -4665,29 +4735,41 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
* to reuse OUTER, it's used for RETURNING in some modify table cases,
* although not INSERT .. CONFLICT).
*/
- if (IsA(ps, SubqueryScanState))
- dpns->inner_planstate = ((SubqueryScanState *) ps)->subplan;
- else if (IsA(ps, CteScanState))
- dpns->inner_planstate = ((CteScanState *) ps)->cteplanstate;
- else if (IsA(ps, ModifyTableState))
+ if (IsA(plan, SubqueryScan))
+ {
+ dpns->inner_planstate = ((SubqueryScanState *)ps)->subplan;
+ dpns->inner_plan = dpns->inner_planstate->plan;
+ }
+ else if (IsA(plan, CteScan))
+ {
+ dpns->inner_planstate = ((CteScanState *)ps)->cteplanstate;
+ dpns->inner_plan = dpns->inner_planstate->plan;
+ }
+ else if (IsA(plan, ModifyTable))
+ {
+ dpns->inner_plan = plan;
dpns->inner_planstate = ps;
+ }
else
- dpns->inner_planstate = innerPlanState(ps);
+ {
+ dpns->inner_plan = innerPlan(plan);
+ dpns->inner_planstate = ps != NULL ? innerPlanState(ps) : NULL;
+ }
- if (IsA(ps, ModifyTableState))
- dpns->inner_tlist = ((ModifyTableState *) ps)->mt_excludedtlist;
- else if (dpns->inner_planstate)
- dpns->inner_tlist = dpns->inner_planstate->plan->targetlist;
+ if (IsA(plan, ModifyTable))
+ dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
+ else if (dpns->inner_plan)
+ dpns->inner_tlist = dpns->inner_plan->targetlist;
else
dpns->inner_tlist = NIL;
/* Set up referent for INDEX_VAR Vars, if needed */
- if (IsA(ps->plan, IndexOnlyScan))
- dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist;
- else if (IsA(ps->plan, ForeignScan))
- dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_scan_tlist;
- else if (IsA(ps->plan, CustomScan))
- dpns->index_tlist = ((CustomScan *) ps->plan)->custom_scan_tlist;
+ if (IsA(plan, IndexOnlyScan))
+ dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
+ else if (IsA(plan, ForeignScan))
+ dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
+ else if (IsA(plan, CustomScan))
+ dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
else
dpns->index_tlist = NIL;
}
@@ -4705,17 +4787,18 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
* previous state for pop_child_plan.
*/
static void
-push_child_plan(deparse_namespace *dpns, PlanState *ps,
+push_child_plan(deparse_namespace *dpns, PlanState *ps, Plan *plan,
deparse_namespace *save_dpns)
{
/* Save state for restoration later */
*save_dpns = *dpns;
- /* Link current plan node into ancestors list */
+ /* Link current plan node into ancestors and ancestor_plans lists */
dpns->ancestors = lcons(dpns->planstate, dpns->ancestors);
+ dpns->ancestor_plans = lcons(dpns->plan, dpns->ancestor_plans);
/* Set attention on selected child */
- set_deparse_planstate(dpns, ps);
+ set_deparse_plan(dpns, ps, plan);
}
/*
@@ -4725,15 +4808,22 @@ static void
pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
{
List *ancestors;
+ List *ancestor_plans;
- /* Get rid of ancestors list cell added by push_child_plan */
+ /* Get rid of ancestors list cells added by push_child_plan */
ancestors = list_delete_first(dpns->ancestors);
+ ancestor_plans = list_delete_first(dpns->ancestor_plans);
/* Restore fields changed by push_child_plan */
*dpns = *save_dpns;
- /* Make sure dpns->ancestors is right (may be unnecessary) */
+ /*
+ * Make sure dpns->ancestors and dpns->ancestor_plans is right (may be
+ * unnecessary)
+ */
dpns->ancestors = ancestors;
+ dpns->ancestor_plans = ancestor_plans;
+
}
/*
@@ -4753,10 +4843,12 @@ pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
*/
static void
push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
- deparse_namespace *save_dpns)
+ ListCell *ancestor_plan_cell, deparse_namespace *save_dpns)
{
- PlanState *ps = (PlanState *) lfirst(ancestor_cell);
+ PlanState *planstate = (PlanState *) lfirst(ancestor_cell);
+ Plan *plan = (Plan *) lfirst(ancestor_plan_cell);
List *ancestors;
+ List *ancestor_plans;
/* Save state for restoration later */
*save_dpns = *dpns;
@@ -4767,8 +4859,14 @@ push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
ancestors = lappend(ancestors, lfirst(ancestor_cell));
dpns->ancestors = ancestors;
+ /* and same for the ancestor_plans list */
+ ancestor_plans = NIL;
+ while ((ancestor_plan_cell = lnext(ancestor_plan_cell)) != NULL)
+ ancestor_plans = lappend(ancestor_plans, lfirst(ancestor_plan_cell));
+ dpns->ancestor_plans = ancestor_plans;
+
/* Set attention on selected ancestor */
- set_deparse_planstate(dpns, ps);
+ set_deparse_plan(dpns, planstate, plan);
}
/*
@@ -6702,7 +6800,7 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
*/
if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
attnum > list_length(rte->eref->colnames) &&
- dpns->inner_planstate)
+ dpns->inner_plan)
{
TargetEntry *tle;
deparse_namespace save_dpns;
@@ -6713,7 +6811,7 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
var->varattno, rte->eref->aliasname);
Assert(netlevelsup == 0);
- push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
+ push_child_plan(dpns, dpns->inner_planstate, dpns->inner_plan, &save_dpns);
/*
* Force parentheses because our caller probably assumed a Var is a
@@ -6863,7 +6961,7 @@ resolve_special_varno(Node *node, deparse_context *context, void *private,
if (!tle)
elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
- push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
+ push_child_plan(dpns, dpns->outer_planstate, dpns->outer_plan, &save_dpns);
resolve_special_varno((Node *) tle->expr, context, private, callback);
pop_child_plan(dpns, &save_dpns);
return;
@@ -6877,7 +6975,7 @@ resolve_special_varno(Node *node, deparse_context *context, void *private,
if (!tle)
elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
- push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
+ push_child_plan(dpns, dpns->inner_planstate, dpns->inner_plan, &save_dpns);
resolve_special_varno((Node *) tle->expr, context, private, callback);
pop_child_plan(dpns, &save_dpns);
return;
@@ -6947,15 +7045,18 @@ get_name_for_var_field(Var *var, int fieldno,
{
Param *param = (Param *) var;
ListCell *ancestor_cell;
+ ListCell *ancestor_plan_cell;
- expr = find_param_referent(param, context, &dpns, &ancestor_cell);
+ expr = find_param_referent(param, context, &dpns, &ancestor_cell,
+ &ancestor_plan_cell);
if (expr)
{
/* Found a match, so recurse to decipher the field name */
deparse_namespace save_dpns;
const char *result;
- push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
+ push_ancestor_plan(dpns, ancestor_cell, ancestor_plan_cell,
+ &save_dpns);
result = get_name_for_var_field((Var *) expr, fieldno,
0, context);
pop_ancestor_plan(dpns, &save_dpns);
@@ -7005,7 +7106,8 @@ get_name_for_var_field(Var *var, int fieldno,
elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
- push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
+ push_child_plan(dpns, dpns->outer_planstate, dpns->outer_plan,
+ &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
@@ -7024,7 +7126,8 @@ get_name_for_var_field(Var *var, int fieldno,
elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
- push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
+ push_child_plan(dpns, dpns->inner_planstate, dpns->inner_plan,
+ &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
@@ -7134,7 +7237,7 @@ get_name_for_var_field(Var *var, int fieldno,
deparse_namespace save_dpns;
const char *result;
- if (!dpns->inner_planstate)
+ if (!dpns->inner_plan)
elog(ERROR, "failed to find plan for subquery %s",
rte->eref->aliasname);
tle = get_tle_by_resno(dpns->inner_tlist, attnum);
@@ -7142,7 +7245,8 @@ get_name_for_var_field(Var *var, int fieldno,
elog(ERROR, "bogus varattno for subquery var: %d",
attnum);
Assert(netlevelsup == 0);
- push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
+ push_child_plan(dpns, dpns->inner_planstate,
+ dpns->inner_plan, &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
@@ -7252,7 +7356,7 @@ get_name_for_var_field(Var *var, int fieldno,
deparse_namespace save_dpns;
const char *result;
- if (!dpns->inner_planstate)
+ if (!dpns->inner_plan)
elog(ERROR, "failed to find plan for CTE %s",
rte->eref->aliasname);
tle = get_tle_by_resno(dpns->inner_tlist, attnum);
@@ -7260,7 +7364,8 @@ get_name_for_var_field(Var *var, int fieldno,
elog(ERROR, "bogus varattno for subquery var: %d",
attnum);
Assert(netlevelsup == 0);
- push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
+ push_child_plan(dpns, dpns->inner_planstate,
+ dpns->inner_plan, &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
@@ -7286,13 +7391,14 @@ get_name_for_var_field(Var *var, int fieldno,
* Try to find the referenced expression for a PARAM_EXEC Param that might
* reference a parameter supplied by an upper NestLoop or SubPlan plan node.
*
- * If successful, return the expression and set *dpns_p and *ancestor_cell_p
- * appropriately for calling push_ancestor_plan(). If no referent can be
- * found, return NULL.
+ * If successful, return the expression and set *dpns_p, *ancestor_cell_p
+ * and ancestor_plan_cell_p appropriately for calling push_ancestor_plan().
+ * If no referent can be found, return NULL.
*/
static Node *
find_param_referent(Param *param, deparse_context *context,
- deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
+ deparse_namespace **dpns_p, ListCell **ancestor_cell_p,
+ ListCell **ancestor_plan_cell_p)
{
/* Initialize output parameters to prevent compiler warnings */
*dpns_p = NULL;
@@ -7309,12 +7415,13 @@ find_param_referent(Param *param, deparse_context *context,
PlanState *child_ps;
bool in_same_plan_level;
ListCell *lc;
+ ListCell *lcp;
dpns = (deparse_namespace *) linitial(context->namespaces);
child_ps = dpns->planstate;
in_same_plan_level = true;
- foreach(lc, dpns->ancestors)
+ forboth(lc, dpns->ancestors, lcp, dpns->ancestor_plans)
{
PlanState *ps = (PlanState *) lfirst(lc);
ListCell *lc2;
@@ -7339,6 +7446,7 @@ find_param_referent(Param *param, deparse_context *context,
/* Found a match, so return it */
*dpns_p = dpns;
*ancestor_cell_p = lc;
+ *ancestor_plan_cell_p = lcp;
return (Node *) nlp->paramval;
}
}
@@ -7368,6 +7476,7 @@ find_param_referent(Param *param, deparse_context *context,
/* Found a match, so return it */
*dpns_p = dpns;
*ancestor_cell_p = lc;
+ *ancestor_plan_cell_p = lcp;
return arg;
}
}
@@ -7416,6 +7525,7 @@ get_parameter(Param *param, deparse_context *context)
Node *expr;
deparse_namespace *dpns;
ListCell *ancestor_cell;
+ ListCell *ancestor_plan_cell;
/*
* If it's a PARAM_EXEC parameter, try to locate the expression from which
@@ -7423,7 +7533,8 @@ get_parameter(Param *param, deparse_context *context)
* an error, since the Param might well be a subplan output rather than an
* input.
*/
- expr = find_param_referent(param, context, &dpns, &ancestor_cell);
+ expr = find_param_referent(param, context, &dpns, &ancestor_cell,
+ &ancestor_plan_cell);
if (expr)
{
/* Found a match, so print it */
@@ -7432,7 +7543,8 @@ get_parameter(Param *param, deparse_context *context)
bool need_paren;
/* Switch attention to the ancestor plan node */
- push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
+ push_ancestor_plan(dpns, ancestor_cell, ancestor_plan_cell,
+ &save_dpns);
/*
* Force prefixing of Vars, since they won't belong to the relation
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index ff3328752e..67ce8e343b 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1185,8 +1185,6 @@ struct AppendState
* slots current output tuple of each subplan
* heap heap of active tuples
* initialized true if we have fetched first tuple from each subplan
- * noopscan true if partition pruning proved that none of the
- * mergeplans can contain a record to satisfy this query.
* prune_state details required to allow partitions to be
* eliminated from the scan, or NULL if not possible.
* valid_subplans for runtime pruning, valid mergeplans indexes to
@@ -1203,7 +1201,6 @@ typedef struct MergeAppendState
TupleTableSlot **ms_slots; /* array of length ms_nplans */
struct binaryheap *ms_heap; /* binary heap of slot indices */
bool ms_initialized; /* are subplans started? */
- bool ms_noopscan;
struct PartitionPruneState *ms_prune_state;
Bitmapset *ms_valid_subplans;
} MergeAppendState;
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 7c49e9d0a8..f4b846bf6d 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -29,8 +29,9 @@ extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit);
extern List *deparse_context_for(const char *aliasname, Oid relid);
extern List *deparse_context_for_plan_rtable(List *rtable, List *rtable_names);
-extern List *set_deparse_context_planstate(List *dpcontext,
- Node *planstate, List *ancestors);
+extern List *set_deparse_context_plan(List *dpcontext,
+ Node *planstatee, Plan *plan,
+ List *ancestors);
extern List *select_rtable_names_for_explain(List *rtable,
Bitmapset *rels_used);
extern char *generate_collation_name(Oid collid);
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 0789b316eb..144e62b548 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -2058,20 +2058,17 @@ select explain_parallel_append('execute ab_q5 (2, 3, 3)');
(19 rows)
-- Try some params whose values do not belong to any partition.
--- We'll still get a single subplan in this case, but it should not be scanned.
select explain_parallel_append('execute ab_q5 (33, 44, 55)');
- explain_parallel_append
--------------------------------------------------------------------------------
+ explain_parallel_append
+-----------------------------------------------------------
Finalize Aggregate (actual rows=1 loops=1)
-> Gather (actual rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (actual rows=1 loops=3)
-> Parallel Append (actual rows=0 loops=N)
- Subplans Removed: 8
- -> Parallel Seq Scan on ab_a1_b1 (never executed)
- Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
-(9 rows)
+ Subplans Removed: 9
+(7 rows)
-- Test Parallel Append with PARAM_EXEC Params
select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2');
@@ -2961,16 +2958,13 @@ explain (analyze, costs off, summary off, timing off) execute q1 (2,2);
Filter: (b = ANY (ARRAY[$1, $2]))
(4 rows)
--- Try with no matching partitions. One subplan should remain in this case,
--- but it shouldn't be executed.
+-- Try with no matching partitions.
explain (analyze, costs off, summary off, timing off) execute q1 (0,0);
- QUERY PLAN
-----------------------------------------------
+ QUERY PLAN
+--------------------------------
Append (actual rows=0 loops=1)
- Subplans Removed: 1
- -> Seq Scan on listp_1_1 (never executed)
- Filter: (b = ANY (ARRAY[$1, $2]))
-(4 rows)
+ Subplans Removed: 2
+(2 rows)
deallocate q1;
-- Test more complex cases where a not-equal condition further eliminates partitions.
@@ -3011,15 +3005,12 @@ explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,0);
(4 rows)
-- Both partitions allowed by IN clause, then both excluded again by <> clauses.
--- One subplan will remain in this case, but it should not be executed.
explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,1);
- QUERY PLAN
--------------------------------------------------------------------------
+ QUERY PLAN
+--------------------------------
Append (actual rows=0 loops=1)
- Subplans Removed: 1
- -> Seq Scan on listp_1_1 (never executed)
- Filter: ((b = ANY (ARRAY[$1, $2])) AND ($3 <> b) AND ($4 <> b))
-(4 rows)
+ Subplans Removed: 2
+(2 rows)
-- Ensure Params that evaluate to NULL properly prune away all partitions
explain (analyze, costs off, summary off, timing off)
@@ -3168,14 +3159,12 @@ execute mt_q1(25);
-- Ensure MergeAppend behaves correctly when no subplans match
explain (analyze, costs off, summary off, timing off) execute mt_q1(35);
- QUERY PLAN
-------------------------------------------------------------------------
+ QUERY PLAN
+--------------------------------------
Merge Append (actual rows=0 loops=1)
Sort Key: ma_test_p1.b
- Subplans Removed: 2
- -> Index Scan using ma_test_p1_b_idx on ma_test_p1 (never executed)
- Filter: ((a >= $1) AND ((a % 10) = 5))
-(5 rows)
+ Subplans Removed: 3
+(3 rows)
execute mt_q1(35);
a
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index c30e58eef7..f2912d9afe 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -485,7 +485,6 @@ select explain_parallel_append('execute ab_q5 (1, 1, 1)');
select explain_parallel_append('execute ab_q5 (2, 3, 3)');
-- Try some params whose values do not belong to any partition.
--- We'll still get a single subplan in this case, but it should not be scanned.
select explain_parallel_append('execute ab_q5 (33, 44, 55)');
-- Test Parallel Append with PARAM_EXEC Params
@@ -726,8 +725,7 @@ explain (analyze, costs off, summary off, timing off) execute q1 (1,1);
explain (analyze, costs off, summary off, timing off) execute q1 (2,2);
--- Try with no matching partitions. One subplan should remain in this case,
--- but it shouldn't be executed.
+-- Try with no matching partitions.
explain (analyze, costs off, summary off, timing off) execute q1 (0,0);
deallocate q1;
@@ -745,7 +743,6 @@ execute q1 (1,2,3,4);
explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,0);
-- Both partitions allowed by IN clause, then both excluded again by <> clauses.
--- One subplan will remain in this case, but it should not be executed.
explain (analyze, costs off, summary off, timing off) execute q1 (1,2,2,1);
-- Ensure Params that evaluate to NULL properly prune away all partitions