summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/commands/explain.c102
-rw-r--r--src/backend/optimizer/plan/createplan.c117
-rw-r--r--src/backend/optimizer/plan/setrefs.c2
3 files changed, 183 insertions, 38 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 8345bc0264b..207f86f1d39 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -147,6 +147,7 @@ static void show_buffer_usage(ExplainState *es, const BufferUsage *usage);
static void show_wal_usage(ExplainState *es, const WalUsage *usage);
static void show_memory_counters(ExplainState *es,
const MemoryContextCounters *mem_counters);
+static void show_result_replacement_info(Result *result, ExplainState *es);
static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
ExplainState *es);
static void ExplainScanTarget(Scan *plan, ExplainState *es);
@@ -1229,6 +1230,10 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
*rels_used = bms_add_members(*rels_used,
((MergeAppend *) plan)->apprelids);
break;
+ case T_Result:
+ *rels_used = bms_add_members(*rels_used,
+ ((Result *) plan)->relids);
+ break;
default:
break;
}
@@ -2232,6 +2237,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
ancestors, es);
break;
case T_Result:
+ show_result_replacement_info(castNode(Result, plan), es);
show_upper_qual((List *) ((Result *) plan)->resconstantqual,
"One-Time Filter", planstate, ancestors, es);
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
@@ -4751,6 +4757,102 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
}
/*
+ * Explain what a "Result" node replaced.
+ */
+static void
+show_result_replacement_info(Result *result, ExplainState *es)
+{
+ StringInfoData buf;
+ int nrels = 0;
+ int rti = -1;
+ bool found_non_result = false;
+ char *replacement_type = "???";
+
+ /* If the Result node has a subplan, it didn't replace anything. */
+ if (result->plan.lefttree != NULL)
+ return;
+
+ /* Gating result nodes should have a subplan, and we don't. */
+ Assert(result->result_type != RESULT_TYPE_GATING);
+
+ switch (result->result_type)
+ {
+ case RESULT_TYPE_GATING:
+ replacement_type = "Gating";
+ break;
+ case RESULT_TYPE_SCAN:
+ replacement_type = "Scan";
+ break;
+ case RESULT_TYPE_JOIN:
+ replacement_type = "Join";
+ break;
+ case RESULT_TYPE_UPPER:
+ /* a small white lie */
+ replacement_type = "Aggregate";
+ break;
+ case RESULT_TYPE_MINMAX:
+ replacement_type = "MinMaxAggregate";
+ break;
+ }
+
+ /*
+ * Build up a comma-separated list of user-facing names for the range
+ * table entries in the relids set.
+ */
+ initStringInfo(&buf);
+ while ((rti = bms_next_member(result->relids, rti)) >= 0)
+ {
+ RangeTblEntry *rte = rt_fetch(rti, es->rtable);
+ char *refname;
+
+ /*
+ * add_outer_joins_to_relids will add join RTIs to the relids set of a
+ * join; if that join is then replaced with a Result node, we may see
+ * such RTIs here. But we want to completely ignore those here,
+ * because "a LEFT JOIN b ON whatever" is a join between a and b, not
+ * a join between a, b, and an unnamed join.
+ */
+ if (rte->rtekind == RTE_JOIN)
+ continue;
+
+ /* Count the number of rels that aren't ignored completely. */
+ ++nrels;
+
+ /* Work out what reference name to use and add it to the string. */
+ refname = (char *) list_nth(es->rtable_names, rti - 1);
+ if (refname == NULL)
+ refname = rte->eref->aliasname;
+ if (buf.len > 0)
+ appendStringInfoString(&buf, ", ");
+ appendStringInfoString(&buf, refname);
+
+ /* Keep track of whether we see anything other than RTE_RESULT. */
+ if (rte->rtekind != RTE_RESULT)
+ found_non_result = true;
+ }
+
+ /*
+ * If this Result node is because of a single RTE that is RTE_RESULT, it
+ * is not really replacing anything at all, because there's no other
+ * method for implementing a scan of such an RTE, so we don't display the
+ * Replaces line in such cases.
+ */
+ if (nrels <= 1 && !found_non_result &&
+ result->result_type == RESULT_TYPE_SCAN)
+ return;
+
+ /* Say what we replaced, with list of rels if available. */
+ if (buf.len == 0)
+ ExplainPropertyText("Replaces", replacement_type, es);
+ else
+ {
+ char *s = psprintf("%s on %s", replacement_type, buf.data);
+
+ ExplainPropertyText("Replaces", s, es);
+ }
+}
+
+/*
* Explain the constituent plans of an Append, MergeAppend,
* BitmapAnd, or BitmapOr node.
*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 6791cbeb416..c9dba7ff346 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -99,7 +99,8 @@ static Gather *create_gather_plan(PlannerInfo *root, GatherPath *best_path);
static Plan *create_projection_plan(PlannerInfo *root,
ProjectionPath *best_path,
int flags);
-static Plan *inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe);
+static Plan *inject_projection_plan(Plan *subplan, List *tlist,
+ bool parallel_safe);
static Sort *create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags);
static IncrementalSort *create_incrementalsort_plan(PlannerInfo *root,
IncrementalSortPath *best_path, int flags);
@@ -302,7 +303,10 @@ static SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy,
List *tlist, Plan *lefttree, Plan *righttree,
List *groupList, long numGroups);
static LockRows *make_lockrows(Plan *lefttree, List *rowMarks, int epqParam);
-static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
+static Result *make_gating_result(List *tlist, Node *resconstantqual,
+ Plan *subplan);
+static Result *make_one_row_result(List *tlist, Node *resconstantqual,
+ RelOptInfo *rel);
static ProjectSet *make_project_set(List *tlist, Plan *subplan);
static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan,
CmdType operation, bool canSetTag,
@@ -1012,37 +1016,37 @@ static Plan *
create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
List *gating_quals)
{
- Plan *gplan;
- Plan *splan;
+ Result *gplan;
Assert(gating_quals);
/*
- * We might have a trivial Result plan already. Stacking one Result atop
- * another is silly, so if that applies, just discard the input plan.
+ * Since we need a Result node anyway, always return the path's requested
+ * tlist; that's never a wrong choice, even if the parent node didn't ask
+ * for CP_EXACT_TLIST.
+ */
+ gplan = make_gating_result(build_path_tlist(root, path),
+ (Node *) gating_quals, plan);
+
+ /*
+ * We might have had a trivial Result plan already. Stacking one Result
+ * atop another is silly, so if that applies, just discard the input plan.
* (We're assuming its targetlist is uninteresting; it should be either
- * the same as the result of build_path_tlist, or a simplified version.)
+ * the same as the result of build_path_tlist, or a simplified version.
+ * However, we preserve the set of relids that it purports to scan and
+ * attribute that to our replacement Result instead, and likewise for the
+ * result_type.)
*/
- splan = plan;
if (IsA(plan, Result))
{
Result *rplan = (Result *) plan;
- if (rplan->plan.lefttree == NULL &&
- rplan->resconstantqual == NULL)
- splan = NULL;
+ gplan->plan.lefttree = NULL;
+ gplan->relids = rplan->relids;
+ gplan->result_type = rplan->result_type;
}
/*
- * Since we need a Result node anyway, always return the path's requested
- * tlist; that's never a wrong choice, even if the parent node didn't ask
- * for CP_EXACT_TLIST.
- */
- gplan = (Plan *) make_result(build_path_tlist(root, path),
- (Node *) gating_quals,
- splan);
-
- /*
* Notice that we don't change cost or size estimates when doing gating.
* The costs of qual eval were already included in the subplan's cost.
* Leaving the size alone amounts to assuming that the gating qual will
@@ -1054,12 +1058,12 @@ create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
* in most cases we have only a very bad idea of the probability of the
* gating qual being true.
*/
- copy_plan_costsize(gplan, plan);
+ copy_plan_costsize(&gplan->plan, plan);
/* Gating quals could be unsafe, so better use the Path's safety flag */
- gplan->parallel_safe = path->parallel_safe;
+ gplan->plan.parallel_safe = path->parallel_safe;
- return gplan;
+ return &gplan->plan;
}
/*
@@ -1235,10 +1239,10 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
/* Generate a Result plan with constant-FALSE gating qual */
Plan *plan;
- plan = (Plan *) make_result(tlist,
- (Node *) list_make1(makeBoolConst(false,
- false)),
- NULL);
+ plan = (Plan *) make_one_row_result(tlist,
+ (Node *) list_make1(makeBoolConst(false,
+ false)),
+ best_path->path.parent);
copy_generic_path_info(plan, (Path *) best_path);
@@ -1636,7 +1640,7 @@ create_group_result_plan(PlannerInfo *root, GroupResultPath *best_path)
/* best_path->quals is just bare clauses */
quals = order_qual_clauses(root, best_path->quals);
- plan = make_result(tlist, (Node *) quals, NULL);
+ plan = make_one_row_result(tlist, (Node *) quals, best_path->path.parent);
copy_generic_path_info(&plan->plan, (Path *) best_path);
@@ -1933,8 +1937,7 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags)
}
else
{
- /* We need a Result node */
- plan = (Plan *) make_result(tlist, NULL, subplan);
+ plan = (Plan *) make_gating_result(tlist, NULL, subplan);
copy_generic_path_info(plan, (Path *) best_path);
}
@@ -1958,7 +1961,7 @@ inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe)
{
Plan *plan;
- plan = (Plan *) make_result(tlist, NULL, subplan);
+ plan = (Plan *) make_gating_result(tlist, NULL, subplan);
/*
* In principle, we should charge tlist eval cost plus cpu_per_tuple per
@@ -2436,7 +2439,9 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
/* Generate the output plan --- basically just a Result */
tlist = build_path_tlist(root, &best_path->path);
- plan = make_result(tlist, (Node *) best_path->quals, NULL);
+ plan = make_one_row_result(tlist, (Node *) best_path->quals,
+ best_path->path.parent);
+ plan->result_type = RESULT_TYPE_MINMAX;
copy_generic_path_info(&plan->plan, (Path *) best_path);
@@ -3887,7 +3892,8 @@ create_resultscan_plan(PlannerInfo *root, Path *best_path,
replace_nestloop_params(root, (Node *) scan_clauses);
}
- scan_plan = make_result(tlist, (Node *) scan_clauses, NULL);
+ scan_plan = make_one_row_result(tlist, (Node *) scan_clauses,
+ best_path->parent);
copy_generic_path_info(&scan_plan->plan, best_path);
@@ -6922,22 +6928,57 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
}
/*
- * make_result
- * Build a Result plan node
+ * make_gating_result
+ * Build a Result plan node that performs projection of a subplan, and/or
+ * applies a one time filter (resconstantqual)
*/
static Result *
-make_result(List *tlist,
- Node *resconstantqual,
- Plan *subplan)
+make_gating_result(List *tlist,
+ Node *resconstantqual,
+ Plan *subplan)
{
Result *node = makeNode(Result);
Plan *plan = &node->plan;
+ Assert(subplan != NULL);
+
plan->targetlist = tlist;
plan->qual = NIL;
plan->lefttree = subplan;
plan->righttree = NULL;
+ node->result_type = RESULT_TYPE_GATING;
+ node->resconstantqual = resconstantqual;
+ node->relids = NULL;
+
+ return node;
+}
+
+/*
+ * make_one_row_result
+ * Build a Result plan node that returns a single row (or possibly no rows,
+ * if the one-time filtered defined by resconstantqual returns false)
+ *
+ * 'rel' should be this path's RelOptInfo. In essence, we're saying that this
+ * Result node generates all the tuples for that RelOptInfo. Note that the same
+ * consideration can never arise in make_gating_result(), because in that case
+ * the tuples are always coming from some subordinate node.
+ */
+static Result *
+make_one_row_result(List *tlist,
+ Node *resconstantqual,
+ RelOptInfo *rel)
+{
+ Result *node = makeNode(Result);
+ Plan *plan = &node->plan;
+
+ plan->targetlist = tlist;
+ plan->qual = NIL;
+ plan->lefttree = NULL;
+ plan->righttree = NULL;
+ node->result_type = IS_UPPER_REL(rel) ? RESULT_TYPE_UPPER :
+ IS_JOIN_REL(rel) ? RESULT_TYPE_JOIN : RESULT_TYPE_SCAN;
node->resconstantqual = resconstantqual;
+ node->relids = rel->relids;
return node;
}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index d706546f332..6950eff2c5b 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1056,6 +1056,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
/* resconstantqual can't contain any subplan variable refs */
splan->resconstantqual =
fix_scan_expr(root, splan->resconstantqual, rtoffset, 1);
+ /* adjust the relids set */
+ splan->relids = offset_relid_set(splan->relids, rtoffset);
}
break;
case T_ProjectSet: