提交 8e4b2f67 编写于 作者: T Tom Lane 提交者: Jesse Zhang

Fix up ruleutils.c for CTE features. The main problem was that

get_name_for_var_field didn't have enough context to interpret a reference to
a CTE query's output.  Fixing this requires separate hacks for the regular
deparse case (pg_get_ruledef) and for the EXPLAIN case, since the available
context information is quite different.  It's pretty nearly parallel to the
existing code for SUBQUERY RTEs, though.  Also, add code to make sure we
qualify a relation name that matches a CTE name; else the CTE will mistakenly
capture the reference when reloading the rule.

In passing, fix a pre-existing problem with get_name_for_var_field not working
on variables in targetlists of SubqueryScan plan nodes.  Although latent all
along, this wasn't a problem until we made EXPLAIN VERBOSE try to print
targetlists.  To do this, refactor the deparse_context_for_plan API so that
the special case for SubqueryScan is all on ruleutils.c's side.

(cherry picked from commit 742fd06d)
上级 391e9ea7
......@@ -9,7 +9,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.179 2008/10/04 21:56:52 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.180 2008/10/06 20:29:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -100,7 +100,7 @@ static void explain_outNode(StringInfo str,
Plan *outer_plan, Plan *parentPlan,
int indent, ExplainState *es);
static void show_scan_qual(List *qual, const char *qlabel,
int scanrelid, Plan *outer_plan, Plan *inner_plan,
int scanrelid, Plan *scan_plan, Plan *outer_plan,
StringInfo str, int indent, ExplainState *es);
static void show_upper_qual(List *qual, const char *qlabel, Plan *plan,
StringInfo str, int indent, ExplainState *es);
......@@ -1455,19 +1455,19 @@ explain_outNode(StringInfo str,
show_scan_qual(((IndexScan *) plan)->indexqualorig,
"Index Cond",
((Scan *) plan)->scanrelid,
outer_plan, NULL,
plan, outer_plan,
str, indent, es);
show_scan_qual(plan->qual,
"Filter",
((Scan *) plan)->scanrelid,
outer_plan, NULL,
plan, outer_plan,
str, indent, es);
break;
case T_BitmapIndexScan:
show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
"Index Cond",
((Scan *) plan)->scanrelid,
outer_plan, NULL,
plan, outer_plan,
str, indent, es);
break;
case T_BitmapHeapScan:
......@@ -1479,7 +1479,7 @@ explain_outNode(StringInfo str,
show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
"Recheck Cond",
((Scan *) plan)->scanrelid,
outer_plan, NULL,
plan, outer_plan,
str, indent, es);
}
else if (nodeTag(plan) == T_BitmapAppendOnlyScan)
......@@ -1487,7 +1487,7 @@ explain_outNode(StringInfo str,
show_scan_qual(((BitmapAppendOnlyScan *) plan)->bitmapqualorig,
"Recheck Cond",
((Scan *) plan)->scanrelid,
outer_plan, NULL,
plan, outer_plan,
str, indent, es);
}
else if (nodeTag(plan) == T_BitmapTableScan)
......@@ -1495,7 +1495,7 @@ explain_outNode(StringInfo str,
show_scan_qual(((BitmapTableScan *) plan)->bitmapqualorig,
"Recheck Cond",
((Scan *) plan)->scanrelid,
outer_plan, NULL,
plan, outer_plan,
str, indent, es);
}
/* FALL THRU */
......@@ -1512,15 +1512,14 @@ explain_outNode(StringInfo str,
show_scan_qual(plan->qual,
"Filter",
((Scan *) plan)->scanrelid,
outer_plan, NULL,
plan, outer_plan,
str, indent, es);
break;
case T_SubqueryScan:
show_scan_qual(plan->qual,
"Filter",
((Scan *) plan)->scanrelid,
outer_plan,
((SubqueryScan *) plan)->subplan,
plan, outer_plan,
str, indent, es);
break;
case T_TidScan:
......@@ -1536,12 +1535,12 @@ explain_outNode(StringInfo str,
show_scan_qual(tidquals,
"TID Cond",
((Scan *) plan)->scanrelid,
outer_plan, NULL,
plan, outer_plan,
str, indent, es);
show_scan_qual(plan->qual,
"Filter",
((Scan *) plan)->scanrelid,
outer_plan, NULL,
plan, outer_plan,
str, indent, es);
}
break;
......@@ -1971,12 +1970,11 @@ explain_outNode(StringInfo str,
* Show a qualifier expression for a scan plan node
*
* Note: outer_plan is the referent for any OUTER vars in the scan qual;
* this would be the outer side of a nestloop plan. inner_plan should be
* NULL except for a SubqueryScan plan node, where it should be the subplan.
* this would be the outer side of a nestloop plan. Pass NULL if none.
*/
static void
show_scan_qual(List *qual, const char *qlabel,
int scanrelid, Plan *outer_plan, Plan *inner_plan,
int scanrelid, Plan *scan_plan, Plan *outer_plan,
StringInfo str, int indent, ExplainState *es)
{
List *context;
......@@ -1993,10 +1991,11 @@ show_scan_qual(List *qual, const char *qlabel,
node = (Node *) make_ands_explicit(qual);
/* Set up deparsing context */
context = deparse_context_for_plan((Node *) outer_plan,
(Node *) inner_plan,
es->rtable);
useprefix = (outer_plan != NULL || inner_plan != NULL);
context = deparse_context_for_plan((Node *) scan_plan,
(Node *) outer_plan,
es->rtable,
es->pstmt->subplans);
useprefix = (outer_plan != NULL || IsA(scan_plan, SubqueryScan));
/* Deparse the expression */
exprstr = deparse_expr_sweet(node, context, useprefix, false);
......@@ -2025,9 +2024,10 @@ show_upper_qual(List *qual, const char *qlabel, Plan *plan,
return;
/* Set up deparsing context */
context = deparse_context_for_plan((Node *) outerPlan(plan),
(Node *) innerPlan(plan),
es->rtable);
context = deparse_context_for_plan((Node *) plan,
NULL,
es->rtable,
es->pstmt->subplans);
useprefix = list_length(es->rtable) > 1;
/* Deparse the expression */
......@@ -2079,9 +2079,10 @@ show_grouping_keys(Plan *plan,
outerPlan = (Node *) llast(((Sequence *) subplan)->subplans);
/* Set up deparse context */
context = deparse_context_for_plan(outerPlan,
innerPlan,
es->rtable);
context = deparse_context_for_plan(subplan,
outerPlan,
es->rtable,
es->pstmt->subplans);
if (IsA(plan, Agg))
{
......@@ -2155,9 +2156,10 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
appendStringInfo(str, " %s: ", qlabel);
/* Set up deparsing context */
context = deparse_context_for_plan((Node *) outerPlan(sortplan),
NULL, /* Sort has no innerPlan */
es->rtable);
context = deparse_context_for_plan((Node *) sortplan,
NULL,
es->rtable,
es->pstmt->subplans);
useprefix = list_length(es->rtable) > 1;
for (keyno = 0; keyno < nkeys; keyno++)
......@@ -2199,9 +2201,9 @@ show_motion_keys(Plan *plan, List *hashExpr, int nkeys, AttrNumber *keycols,
return;
/* Set up deparse context */
context = deparse_context_for_plan((Node *) outerPlan(plan),
NULL, /* Motion has no innerPlan */
es->rtable);
context = deparse_context_for_plan((Node *) plan, (Node *) outerPlan(plan),
es->rtable,
es->pstmt->subplans);
/* Merge Receive ordering key */
if (nkeys > 0)
......@@ -2266,8 +2268,9 @@ explain_partition_selector(PartitionSelector *ps, Plan *parent,
/* Set up deparsing context */
context = deparse_context_for_plan((Node *) parent,
(Node *) parent,
es->rtable);
(Node *) outerPlan(parent),
es->rtable,
es->pstmt->subplans);
useprefix = list_length(es->rtable) > 1;
/* Deparse the expression */
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.285 2008/10/04 21:56:54 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.287 2008/10/06 20:29:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -97,15 +97,19 @@ typedef struct
* A Var having varlevelsup=N refers to the N'th item (counting from 0) in
* the current context's namespaces list.
*
* The rangetable is the list of actual RTEs from the query tree.
* The rangetable is the list of actual RTEs from the query tree, and the
* cte list is the list of actual CTEs.
*
* For deparsing plan trees, we provide for outer and inner subplan nodes.
* The tlists of these nodes are used to resolve OUTER and INNER varnos.
* Also, in the plan-tree case we don't have access to the parse-time CTE
* list, so we need a list of subplans instead.
*/
typedef struct
{
List *rtable; /* List of RangeTblEntry nodes */
List *ctes; /* List of CommonTableExpr nodes */
List *subplans; /* List of subplans, in plan-tree case */
Plan *outer_plan; /* OUTER subplan, or NULL if none */
Plan *inner_plan; /* INNER subplan, or NULL if none */
} deparse_namespace;
......@@ -173,7 +177,8 @@ static void get_rule_groupingclause(GroupingClause *grp, List *tlist,
static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist,
bool force_colno,
deparse_context *context);
static char *get_variable(Var *var, int levelsup, bool istoplevel,
static void push_plan(deparse_namespace *dpns, Plan *subplan);
static char *get_variable(Var *var, int levelsup, bool showstar,
deparse_context *context);
static RangeTblEntry *find_rte_by_refname(const char *refname,
deparse_context *context);
......@@ -1758,6 +1763,7 @@ deparse_context_for(const char *aliasname, Oid relid)
/* Build one-element rtable */
dpns->rtable = list_make1(rte);
dpns->ctes = NIL;
dpns->subplans = NIL;
dpns->outer_plan = dpns->inner_plan = NULL;
/* Return a one-deep namespace stack */
......@@ -1768,21 +1774,27 @@ deparse_context_for(const char *aliasname, Oid relid)
* deparse_context_for_plan - Build deparse context for a plan node
*
* When deparsing an expression in a Plan tree, we might have to resolve
* OUTER or INNER references. Pass the plan nodes whose targetlists define
* such references, or NULL when none are expected. (outer_plan and
* inner_plan really ought to be declared as "Plan *", but we use "Node *"
* to avoid having to include plannodes.h in builtins.h.)
* OUTER or INNER references. To do this, the caller must provide the
* parent Plan node. In the normal case of a join plan node, OUTER and
* INNER references can be resolved by drilling down into the left and
* right child plans. A special case is that a nestloop inner indexscan
* might have OUTER Vars, but the outer side of the join is not a child
* plan node. To handle such cases the outer plan node must be passed
* separately. (Pass NULL for outer_plan otherwise.)
*
* As a special case, when deparsing a SubqueryScan plan, pass the subplan
* as inner_plan (there won't be any regular innerPlan() in this case).
* Note: plan and outer_plan really ought to be declared as "Plan *", but
* we use "Node *" to avoid having to include plannodes.h in builtins.h.
*
* The plan's rangetable list must also be passed. We actually prefer to use
* the rangetable to resolve simple Vars, but the subplan inputs are needed
* the rangetable to resolve simple Vars, but the plan inputs are necessary
* for Vars that reference expressions computed in subplan target lists.
*
* We also need the list of subplans associated with the Plan tree; this
* is for resolving references to CTE subplans.
*/
List *
deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
List *rtable)
deparse_context_for_plan(Node *plan, Node *outer_plan,
List *rtable, List *subplans)
{
deparse_namespace *dpns;
......@@ -1790,8 +1802,35 @@ deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
dpns->rtable = rtable;
dpns->ctes = NIL;
dpns->outer_plan = (Plan *) outer_plan;
dpns->inner_plan = (Plan *) inner_plan;
dpns->subplans = subplans;
dpns->inner_plan = NULL;
/*
* Set up outer_plan and inner_plan from the Plan node (this includes
* various special cases for particular Plan types).
*/
push_plan(dpns, (Plan *) plan);
/*
* If outer_plan is given, that overrides whatever we got from the plan.
*/
if (outer_plan)
dpns->outer_plan = (Plan *) outer_plan;
/*
* Previously, this function was called from explain_partition_selector with
* the Parent node for both Node arguments. A change to the function
* signature requires us to first set the innerplan and detect that it is
* indeed a PartitionSelector in order to then set both outer_plan and
* inner_plan to the parent. A simple check of the parent->lefttree is not
* sufficient since a Sequence operator will have the child nodes in its
* subplans list. Thus, we allow push_plans to assign inner and outer plan
* as usual and then add a check here
*/
if (dpns->inner_plan && IsA(dpns->inner_plan, PartitionSelector))
{
dpns->inner_plan = (Plan *) plan;
dpns->outer_plan = (Plan *) plan;
}
/* Return a one-deep namespace stack */
return list_make1(dpns);
......@@ -1940,6 +1979,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
context.indentLevel = PRETTYINDENT_STD;
dpns.rtable = query->rtable;
dpns.ctes = query->cteList;
dpns.subplans = NIL;
dpns.outer_plan = dpns.inner_plan = NULL;
get_rule_expr(qual, &context, false);
......@@ -2093,6 +2133,7 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
dpns.rtable = query->rtable;
dpns.ctes = query->cteList;
dpns.subplans = NIL;
dpns.outer_plan = dpns.inner_plan = NULL;
switch (query->commandType)
......@@ -3078,6 +3119,8 @@ get_utility_query_def(Query *query, deparse_context *context)
* (although in a Plan tree there really shouldn't be any).
*
* Caller must save and restore outer_plan and inner_plan around this.
*
* We also use this to initialize the fields during deparse_context_for_plan.
*/
static void
push_plan(deparse_namespace *dpns, Plan *subplan)
......@@ -3105,9 +3148,30 @@ push_plan(deparse_namespace *dpns, Plan *subplan)
/*
* For a SubqueryScan, pretend the subplan is INNER referent. (We don't
* use OUTER because that could someday conflict with the normal meaning.)
* Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
*/
if (IsA(subplan, SubqueryScan))
dpns->inner_plan = ((SubqueryScan *) subplan)->subplan;
else if (IsA(subplan, CteScan))
{
int ctePlanId = ((CteScan *) subplan)->ctePlanId;
if (ctePlanId > 0 && ctePlanId <= list_length(dpns->subplans))
dpns->inner_plan = list_nth(dpns->subplans, ctePlanId - 1);
else
dpns->inner_plan = NULL;
}
else if (IsA(subplan, Sequence))
{
/*
* Set the inner_plan to a sequences first child only if it is a
* partition selector. This is a specific fix to enable Explain's of
* query plans that have a Partition Selector
*/
Plan *node = (Plan *) linitial(((Sequence *) subplan)->subplans);
if (IsA(node, PartitionSelector))
dpns->inner_plan = node;
}
else
dpns->inner_plan = innerPlan(subplan);
}
......@@ -3478,8 +3542,8 @@ get_name_for_var_field(Var *var, int fieldno,
* This part has essentially the same logic as the parser's
* expandRecordVariable() function, but we are dealing with a different
* representation of the input context, and we only need one field name
* not a TupleDesc. Also, we need a special case for deparsing Plan
* trees, because the subquery field has been removed from SUBQUERY RTEs.
* not a TupleDesc. Also, we need special cases for finding subquery
* and CTE subplans when deparsing Plan trees.
*/
expr = (Node *) var; /* default if we can't drill down */
......@@ -3496,10 +3560,10 @@ get_name_for_var_field(Var *var, int fieldno,
*/
break;
case RTE_SUBQUERY:
/* Subselect-in-FROM: examine sub-select's output expr */
{
if (rte->subquery)
{
/* Subselect-in-FROM: examine sub-select's output expr */
TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
attnum);
......@@ -3519,6 +3583,8 @@ get_name_for_var_field(Var *var, int fieldno,
const char *result;
mydpns.rtable = rte->subquery->rtable;
mydpns.ctes = rte->subquery->cteList;
mydpns.subplans = NIL;
mydpns.outer_plan = mydpns.inner_plan = NULL;
context->namespaces = lcons(&mydpns,
......@@ -3538,10 +3604,10 @@ get_name_for_var_field(Var *var, int fieldno,
{
/*
* We're deparsing a Plan tree so we don't have complete
* RTE entries. But the only place we'd see a Var
* directly referencing a SUBQUERY RTE is in a
* SubqueryScan plan node, and we can look into the child
* plan's tlist instead.
* RTE entries (in particular, rte->subquery is NULL).
* But the only place we'd see a Var directly referencing
* a SUBQUERY RTE is in a SubqueryScan plan node, and we
* can look into the child plan's tlist instead.
*/
TargetEntry *tle;
Plan *save_outer;
......@@ -3600,7 +3666,7 @@ get_name_for_var_field(Var *var, int fieldno,
/*
* Try to find the referenced CTE using the namespace stack.
*/
ctelevelsup = rte->ctelevelsup + levelsup;
ctelevelsup = rte->ctelevelsup + netlevelsup;
if (ctelevelsup >= list_length(context->namespaces))
lc = NULL;
else
......@@ -3624,21 +3690,20 @@ get_name_for_var_field(Var *var, int fieldno,
if (ste == NULL || ste->resjunk)
{
ereport(WARNING, (errcode(ERRCODE_INTERNAL_ERROR),
errmsg_internal("bogus var: varno=%d varattno=%d",
var->varno, var->varattno) ));
return "*BOGUS*";
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
errmsg_internal(ERROR, "subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
}
expr = (Node *) ste->expr;
if (IsA(expr, Var))
{
/*
* Recurse into the CTE to see what its Var refers to.
* We have to build an additional level of namespace
* to keep in step with varlevelsup in the CTE.
* Furthermore it could be an outer CTE, so we may
* have to delete some levels of namespace.
* Recurse into the CTE to see what its Var refers
* to. We have to build an additional level of
* namespace to keep in step with varlevelsup in the
* CTE. Furthermore it could be an outer CTE, so
* we may have to delete some levels of namespace.
*/
List *save_nslist = context->namespaces;
List *new_nslist;
......@@ -3647,9 +3712,7 @@ get_name_for_var_field(Var *var, int fieldno,
mydpns.rtable = ctequery->rtable;
mydpns.ctes = ctequery->cteList;
#if 0 /* GPDB_84_FIXME: we'll get subplans in 8.4 */
mydpns.subplans = NIL;
#endif
mydpns.outer_plan = mydpns.inner_plan = NULL;
new_nslist = list_copy_tail(context->namespaces,
......@@ -3665,6 +3728,39 @@ get_name_for_var_field(Var *var, int fieldno,
}
/* else fall through to inspect the expression */
}
else
{
/*
* We're deparsing a Plan tree so we don't have a CTE
* list. But the only place we'd see a Var directly
* referencing a CTE RTE is in a CteScan plan node, and
* we can look into the subplan's tlist instead.
*/
TargetEntry *tle;
Plan *save_outer;
Plan *save_inner;
const char *result;
if (!dpns->inner_plan)
elog(ERROR, "failed to find plan for CTE %s",
rte->eref->aliasname);
tle = get_tle_by_resno(dpns->inner_plan->targetlist,
attnum);
if (!tle)
elog(ERROR, "bogus varattno for subquery var: %d",
attnum);
Assert(netlevelsup == 0);
save_outer = dpns->outer_plan;
save_inner = dpns->inner_plan;
push_plan(dpns, dpns->inner_plan);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
dpns->outer_plan = save_outer;
dpns->inner_plan = save_inner;
return result;
}
}
break;
case RTE_VOID:
......@@ -6508,7 +6604,6 @@ generate_relation_name(Oid relid, List *namespaces)
if (!need_qual)
need_qual = !RelationIsVisible(relid);
/* Qualify the name if not visible in search path */
if (need_qual)
nspname = get_namespace_name(reltup->relnamespace);
else
......
......@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.336 2009/08/01 19:59:41 tgl Exp $
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.323 2008/10/06 20:29:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -615,9 +615,9 @@ extern char *deparse_expression(Node *expr, List *dpcontext,
extern char *deparse_expr_sweet(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit); /*CDB*/
extern List *deparse_context_for(const char *aliasname, Oid relid);
extern List *deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
List *rtable);
extern const char *quote_literal_internal(const char *literal);
extern List *deparse_context_for_plan(Node *plan, Node *outer_plan,
List *rtable, List *subplans);
extern const char *quote_identifier(const char *ident);
extern char *quote_qualified_identifier(const char *qualifier,
const char *ident);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册