aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c231
1 files changed, 224 insertions, 7 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index f2893d40861..752757be116 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -437,6 +437,10 @@ static void resolve_special_varno(Node *node, deparse_context *context,
rsv_callback callback, void *callback_arg);
static Node *find_param_referent(Param *param, deparse_context *context,
deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
+static SubPlan *find_param_generator(Param *param, deparse_context *context,
+ int *column_p);
+static SubPlan *find_param_generator_initplan(Param *param, Plan *plan,
+ int *column_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);
@@ -8135,6 +8139,128 @@ find_param_referent(Param *param, deparse_context *context,
}
/*
+ * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.
+ *
+ * If successful, return the generating subplan/initplan and set *column_p
+ * to the subplan's 0-based output column number.
+ * Otherwise, return NULL.
+ */
+static SubPlan *
+find_param_generator(Param *param, deparse_context *context, int *column_p)
+{
+ /* Initialize output parameter to prevent compiler warnings */
+ *column_p = 0;
+
+ /*
+ * If it's a PARAM_EXEC parameter, search the current plan node as well as
+ * ancestor nodes looking for a subplan or initplan that emits the value
+ * for the Param. It could appear in the setParams of an initplan or
+ * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
+ */
+ if (param->paramkind == PARAM_EXEC)
+ {
+ SubPlan *result;
+ deparse_namespace *dpns;
+ ListCell *lc;
+
+ dpns = (deparse_namespace *) linitial(context->namespaces);
+
+ /* First check the innermost plan node's initplans */
+ result = find_param_generator_initplan(param, dpns->plan, column_p);
+ if (result)
+ return result;
+
+ /*
+ * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
+ * which can be referenced by Params elsewhere in the targetlist.
+ * (Such Params should always be in the same targetlist, so there's no
+ * need to do this work at upper plan nodes.)
+ */
+ foreach_node(TargetEntry, tle, dpns->plan->targetlist)
+ {
+ if (tle->expr && IsA(tle->expr, SubPlan))
+ {
+ SubPlan *subplan = (SubPlan *) tle->expr;
+
+ if (subplan->subLinkType == MULTIEXPR_SUBLINK)
+ {
+ foreach_int(paramid, subplan->setParam)
+ {
+ if (paramid == param->paramid)
+ {
+ /* Found a match, so return it. */
+ *column_p = foreach_current_index(paramid);
+ return subplan;
+ }
+ }
+ }
+ }
+ }
+
+ /* No luck, so check the ancestor nodes */
+ foreach(lc, dpns->ancestors)
+ {
+ Node *ancestor = (Node *) lfirst(lc);
+
+ /*
+ * If ancestor is a SubPlan, check the paramIds it provides.
+ */
+ if (IsA(ancestor, SubPlan))
+ {
+ SubPlan *subplan = (SubPlan *) ancestor;
+
+ foreach_int(paramid, subplan->paramIds)
+ {
+ if (paramid == param->paramid)
+ {
+ /* Found a match, so return it. */
+ *column_p = foreach_current_index(paramid);
+ return subplan;
+ }
+ }
+
+ /* SubPlan isn't a kind of Plan, so skip the rest */
+ continue;
+ }
+
+ /*
+ * Otherwise, it's some kind of Plan node, so check its initplans.
+ */
+ result = find_param_generator_initplan(param, (Plan *) ancestor,
+ column_p);
+ if (result)
+ return result;
+
+ /* No luck, crawl up to next ancestor */
+ }
+ }
+
+ /* No generator found */
+ return NULL;
+}
+
+/*
+ * Subroutine for find_param_generator: search one Plan node's initplans
+ */
+static SubPlan *
+find_param_generator_initplan(Param *param, Plan *plan, int *column_p)
+{
+ foreach_node(SubPlan, subplan, plan->initPlan)
+ {
+ foreach_int(paramid, subplan->setParam)
+ {
+ if (paramid == param->paramid)
+ {
+ /* Found a match, so return it. */
+ *column_p = foreach_current_index(paramid);
+ return subplan;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
* Display a Param appropriately.
*/
static void
@@ -8143,12 +8269,13 @@ get_parameter(Param *param, deparse_context *context)
Node *expr;
deparse_namespace *dpns;
ListCell *ancestor_cell;
+ SubPlan *subplan;
+ int column;
/*
* If it's a PARAM_EXEC parameter, try to locate the expression from which
- * the parameter was computed. Note that failing to find a referent isn't
- * an error, since the Param might well be a subplan output rather than an
- * input.
+ * the parameter was computed. This stanza handles only cases in which
+ * the Param represents an input to the subplan we are currently in.
*/
expr = find_param_referent(param, context, &dpns, &ancestor_cell);
if (expr)
@@ -8193,6 +8320,24 @@ get_parameter(Param *param, deparse_context *context)
}
/*
+ * Alternatively, maybe it's a subplan output, which we print as a
+ * reference to the subplan. (We could drill down into the subplan and
+ * print the relevant targetlist expression, but that has been deemed too
+ * confusing since it would violate normal SQL scope rules. Also, we're
+ * relying on this reference to show that the testexpr containing the
+ * Param has anything to do with that subplan at all.)
+ */
+ subplan = find_param_generator(param, context, &column);
+ if (subplan)
+ {
+ appendStringInfo(context->buf, "(%s%s).col%d",
+ subplan->useHashTable ? "hashed " : "",
+ subplan->plan_name, column + 1);
+
+ return;
+ }
+
+ /*
* If it's an external parameter, see if the outermost namespace provides
* function argument names.
*/
@@ -8240,7 +8385,12 @@ get_parameter(Param *param, deparse_context *context)
/*
* Not PARAM_EXEC, or couldn't find referent: just print $N.
+ *
+ * It's a bug if we get here for anything except PARAM_EXTERN Params, but
+ * in production builds printing $N seems more useful than failing.
*/
+ Assert(param->paramkind == PARAM_EXTERN);
+
appendStringInfo(context->buf, "$%d", param->paramid);
}
@@ -8881,12 +9031,79 @@ get_rule_expr(Node *node, deparse_context *context,
* We cannot see an already-planned subplan in rule deparsing,
* only while EXPLAINing a query plan. We don't try to
* reconstruct the original SQL, just reference the subplan
- * that appears elsewhere in EXPLAIN's result.
+ * that appears elsewhere in EXPLAIN's result. It does seem
+ * useful to show the subLinkType and testexpr (if any), and
+ * we also note whether the subplan will be hashed.
*/
- if (subplan->useHashTable)
- appendStringInfo(buf, "(hashed %s)", subplan->plan_name);
+ switch (subplan->subLinkType)
+ {
+ case EXISTS_SUBLINK:
+ appendStringInfoString(buf, "EXISTS(");
+ Assert(subplan->testexpr == NULL);
+ break;
+ case ALL_SUBLINK:
+ appendStringInfoString(buf, "(ALL ");
+ Assert(subplan->testexpr != NULL);
+ break;
+ case ANY_SUBLINK:
+ appendStringInfoString(buf, "(ANY ");
+ Assert(subplan->testexpr != NULL);
+ break;
+ case ROWCOMPARE_SUBLINK:
+ /* Parenthesizing the testexpr seems sufficient */
+ appendStringInfoChar(buf, '(');
+ Assert(subplan->testexpr != NULL);
+ break;
+ case EXPR_SUBLINK:
+ /* No need to decorate these subplan references */
+ appendStringInfoChar(buf, '(');
+ Assert(subplan->testexpr == NULL);
+ break;
+ case MULTIEXPR_SUBLINK:
+ /* MULTIEXPR isn't executed in the normal way */
+ appendStringInfoString(buf, "(rescan ");
+ Assert(subplan->testexpr == NULL);
+ break;
+ case ARRAY_SUBLINK:
+ appendStringInfoString(buf, "ARRAY(");
+ Assert(subplan->testexpr == NULL);
+ break;
+ case CTE_SUBLINK:
+ /* This case is unreachable within expressions */
+ appendStringInfoString(buf, "CTE(");
+ Assert(subplan->testexpr == NULL);
+ break;
+ }
+
+ if (subplan->testexpr != NULL)
+ {
+ deparse_namespace *dpns;
+
+ /*
+ * Push SubPlan into ancestors list while deparsing
+ * testexpr, so that we can handle PARAM_EXEC references
+ * to the SubPlan's paramIds. (This makes it look like
+ * the SubPlan is an "ancestor" of the current plan node,
+ * which is a little weird, but it does no harm.) In this
+ * path, we don't need to mention the SubPlan explicitly,
+ * because the referencing Params will show its existence.
+ */
+ dpns = (deparse_namespace *) linitial(context->namespaces);
+ dpns->ancestors = lcons(subplan, dpns->ancestors);
+
+ get_rule_expr(subplan->testexpr, context, showimplicit);
+ appendStringInfoChar(buf, ')');
+
+ dpns->ancestors = list_delete_first(dpns->ancestors);
+ }
else
- appendStringInfo(buf, "(%s)", subplan->plan_name);
+ {
+ /* No referencing Params, so show the SubPlan's name */
+ if (subplan->useHashTable)
+ appendStringInfo(buf, "hashed %s)", subplan->plan_name);
+ else
+ appendStringInfo(buf, "%s)", subplan->plan_name);
+ }
}
break;