Commit 655aa5b3 authored by Tom Lane's avatar Tom Lane

Now that plans have flat rangetable lists, it's a lot easier to get EXPLAIN to

drill down into subplan targetlists to print the referent expression for an
OUTER or INNER var in an upper plan node.  Hence, make it do that always, and
banish the old hack of showing "?columnN?" when things got too complicated.

Along the way, fix an EXPLAIN bug I introduced by suppressing subqueries from
execution-time range tables: get_name_for_var_field() assumed it could look at
rte->subquery to find out the real type of a RECORD var.  That doesn't work
anymore, but instead we can look at the input plan of the SubqueryScan plan
node.
parent 9cc2a71c
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.158 2007/02/22 23:44:24 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.159 2007/02/23 21:59:44 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -49,11 +49,9 @@ static void explain_outNode(StringInfo str, ...@@ -49,11 +49,9 @@ static void explain_outNode(StringInfo str,
Plan *outer_plan, Plan *outer_plan,
int indent, ExplainState *es); int indent, ExplainState *es);
static void show_scan_qual(List *qual, const char *qlabel, static void show_scan_qual(List *qual, const char *qlabel,
int scanrelid, Plan *outer_plan, int scanrelid, Plan *outer_plan, Plan *inner_plan,
StringInfo str, int indent, ExplainState *es); StringInfo str, int indent, ExplainState *es);
static void show_upper_qual(List *qual, const char *qlabel, static void show_upper_qual(List *qual, const char *qlabel, Plan *plan,
const char *outer_name, Plan *outer_plan,
const char *inner_name, Plan *inner_plan,
StringInfo str, int indent, ExplainState *es); StringInfo str, int indent, ExplainState *es);
static void show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols, static void show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
const char *qlabel, const char *qlabel,
...@@ -725,19 +723,19 @@ explain_outNode(StringInfo str, ...@@ -725,19 +723,19 @@ explain_outNode(StringInfo str,
show_scan_qual(((IndexScan *) plan)->indexqualorig, show_scan_qual(((IndexScan *) plan)->indexqualorig,
"Index Cond", "Index Cond",
((Scan *) plan)->scanrelid, ((Scan *) plan)->scanrelid,
outer_plan, outer_plan, NULL,
str, indent, es); str, indent, es);
show_scan_qual(plan->qual, show_scan_qual(plan->qual,
"Filter", "Filter",
((Scan *) plan)->scanrelid, ((Scan *) plan)->scanrelid,
outer_plan, outer_plan, NULL,
str, indent, es); str, indent, es);
break; break;
case T_BitmapIndexScan: case T_BitmapIndexScan:
show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig, show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
"Index Cond", "Index Cond",
((Scan *) plan)->scanrelid, ((Scan *) plan)->scanrelid,
outer_plan, outer_plan, NULL,
str, indent, es); str, indent, es);
break; break;
case T_BitmapHeapScan: case T_BitmapHeapScan:
...@@ -745,17 +743,24 @@ explain_outNode(StringInfo str, ...@@ -745,17 +743,24 @@ explain_outNode(StringInfo str,
show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig, show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
"Recheck Cond", "Recheck Cond",
((Scan *) plan)->scanrelid, ((Scan *) plan)->scanrelid,
outer_plan, outer_plan, NULL,
str, indent, es); str, indent, es);
/* FALL THRU */ /* FALL THRU */
case T_SeqScan: case T_SeqScan:
case T_SubqueryScan:
case T_FunctionScan: case T_FunctionScan:
case T_ValuesScan: case T_ValuesScan:
show_scan_qual(plan->qual,
"Filter",
((Scan *) plan)->scanrelid,
outer_plan, NULL,
str, indent, es);
break;
case T_SubqueryScan:
show_scan_qual(plan->qual, show_scan_qual(plan->qual,
"Filter", "Filter",
((Scan *) plan)->scanrelid, ((Scan *) plan)->scanrelid,
outer_plan, outer_plan,
((SubqueryScan *) plan)->subplan,
str, indent, es); str, indent, es);
break; break;
case T_TidScan: case T_TidScan:
...@@ -771,67 +776,49 @@ explain_outNode(StringInfo str, ...@@ -771,67 +776,49 @@ explain_outNode(StringInfo str,
show_scan_qual(tidquals, show_scan_qual(tidquals,
"TID Cond", "TID Cond",
((Scan *) plan)->scanrelid, ((Scan *) plan)->scanrelid,
outer_plan, outer_plan, NULL,
str, indent, es); str, indent, es);
show_scan_qual(plan->qual, show_scan_qual(plan->qual,
"Filter", "Filter",
((Scan *) plan)->scanrelid, ((Scan *) plan)->scanrelid,
outer_plan, outer_plan, NULL,
str, indent, es); str, indent, es);
} }
break; break;
case T_NestLoop: case T_NestLoop:
show_upper_qual(((NestLoop *) plan)->join.joinqual, show_upper_qual(((NestLoop *) plan)->join.joinqual,
"Join Filter", "Join Filter", plan,
"outer", outerPlan(plan),
"inner", innerPlan(plan),
str, indent, es); str, indent, es);
show_upper_qual(plan->qual, show_upper_qual(plan->qual,
"Filter", "Filter", plan,
"outer", outerPlan(plan),
"inner", innerPlan(plan),
str, indent, es); str, indent, es);
break; break;
case T_MergeJoin: case T_MergeJoin:
show_upper_qual(((MergeJoin *) plan)->mergeclauses, show_upper_qual(((MergeJoin *) plan)->mergeclauses,
"Merge Cond", "Merge Cond", plan,
"outer", outerPlan(plan),
"inner", innerPlan(plan),
str, indent, es); str, indent, es);
show_upper_qual(((MergeJoin *) plan)->join.joinqual, show_upper_qual(((MergeJoin *) plan)->join.joinqual,
"Join Filter", "Join Filter", plan,
"outer", outerPlan(plan),
"inner", innerPlan(plan),
str, indent, es); str, indent, es);
show_upper_qual(plan->qual, show_upper_qual(plan->qual,
"Filter", "Filter", plan,
"outer", outerPlan(plan),
"inner", innerPlan(plan),
str, indent, es); str, indent, es);
break; break;
case T_HashJoin: case T_HashJoin:
show_upper_qual(((HashJoin *) plan)->hashclauses, show_upper_qual(((HashJoin *) plan)->hashclauses,
"Hash Cond", "Hash Cond", plan,
"outer", outerPlan(plan),
"inner", innerPlan(plan),
str, indent, es); str, indent, es);
show_upper_qual(((HashJoin *) plan)->join.joinqual, show_upper_qual(((HashJoin *) plan)->join.joinqual,
"Join Filter", "Join Filter", plan,
"outer", outerPlan(plan),
"inner", innerPlan(plan),
str, indent, es); str, indent, es);
show_upper_qual(plan->qual, show_upper_qual(plan->qual,
"Filter", "Filter", plan,
"outer", outerPlan(plan),
"inner", innerPlan(plan),
str, indent, es); str, indent, es);
break; break;
case T_Agg: case T_Agg:
case T_Group: case T_Group:
show_upper_qual(plan->qual, show_upper_qual(plan->qual,
"Filter", "Filter", plan,
"subplan", outerPlan(plan),
"", NULL,
str, indent, es); str, indent, es);
break; break;
case T_Sort: case T_Sort:
...@@ -843,14 +830,10 @@ explain_outNode(StringInfo str, ...@@ -843,14 +830,10 @@ explain_outNode(StringInfo str,
break; break;
case T_Result: case T_Result:
show_upper_qual((List *) ((Result *) plan)->resconstantqual, show_upper_qual((List *) ((Result *) plan)->resconstantqual,
"One-Time Filter", "One-Time Filter", plan,
"subplan", outerPlan(plan),
"", NULL,
str, indent, es); str, indent, es);
show_upper_qual(plan->qual, show_upper_qual(plan->qual,
"Filter", "Filter", plan,
"subplan", outerPlan(plan),
"", NULL,
str, indent, es); str, indent, es);
break; break;
default: default:
...@@ -1032,14 +1015,18 @@ explain_outNode(StringInfo str, ...@@ -1032,14 +1015,18 @@ explain_outNode(StringInfo str,
/* /*
* Show a qualifier expression for a scan plan node * 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.
*/ */
static void static void
show_scan_qual(List *qual, const char *qlabel, show_scan_qual(List *qual, const char *qlabel,
int scanrelid, Plan *outer_plan, int scanrelid, Plan *outer_plan, Plan *inner_plan,
StringInfo str, int indent, ExplainState *es) StringInfo str, int indent, ExplainState *es)
{ {
Node *outercontext;
List *context; List *context;
bool useprefix;
Node *node; Node *node;
char *exprstr; char *exprstr;
int i; int i;
...@@ -1051,31 +1038,14 @@ show_scan_qual(List *qual, const char *qlabel, ...@@ -1051,31 +1038,14 @@ show_scan_qual(List *qual, const char *qlabel,
/* Convert AND list to explicit AND */ /* Convert AND list to explicit AND */
node = (Node *) make_ands_explicit(qual); node = (Node *) make_ands_explicit(qual);
/* /* Set up deparsing context */
* If we have an outer plan that is referenced by the qual, add it to the context = deparse_context_for_plan((Node *) outer_plan,
* deparse context. If not, don't (so that we don't force prefixes (Node *) inner_plan,
* unnecessarily).
*/
if (outer_plan)
{
Relids varnos = pull_varnos(node);
if (bms_is_member(OUTER, varnos))
outercontext = deparse_context_for_subplan("outer",
(Node *) outer_plan);
else
outercontext = NULL;
bms_free(varnos);
}
else
outercontext = NULL;
context = deparse_context_for_plan(OUTER, outercontext,
0, NULL,
es->rtable); es->rtable);
useprefix = (outer_plan != NULL || inner_plan != NULL);
/* Deparse the expression */ /* Deparse the expression */
exprstr = deparse_expression(node, context, (outercontext != NULL), false); exprstr = deparse_expression(node, context, useprefix, false);
/* And add to str */ /* And add to str */
for (i = 0; i < indent; i++) for (i = 0; i < indent; i++)
...@@ -1087,16 +1057,11 @@ show_scan_qual(List *qual, const char *qlabel, ...@@ -1087,16 +1057,11 @@ show_scan_qual(List *qual, const char *qlabel,
* Show a qualifier expression for an upper-level plan node * Show a qualifier expression for an upper-level plan node
*/ */
static void static void
show_upper_qual(List *qual, const char *qlabel, show_upper_qual(List *qual, const char *qlabel, Plan *plan,
const char *outer_name, Plan *outer_plan,
const char *inner_name, Plan *inner_plan,
StringInfo str, int indent, ExplainState *es) StringInfo str, int indent, ExplainState *es)
{ {
List *context; List *context;
Node *outercontext; bool useprefix;
Node *innercontext;
int outer_varno;
int inner_varno;
Node *node; Node *node;
char *exprstr; char *exprstr;
int i; int i;
...@@ -1105,36 +1070,15 @@ show_upper_qual(List *qual, const char *qlabel, ...@@ -1105,36 +1070,15 @@ show_upper_qual(List *qual, const char *qlabel,
if (qual == NIL) if (qual == NIL)
return; return;
/* Generate deparse context */ /* Set up deparsing context */
if (outer_plan) context = deparse_context_for_plan((Node *) outerPlan(plan),
{ (Node *) innerPlan(plan),
outercontext = deparse_context_for_subplan(outer_name,
(Node *) outer_plan);
outer_varno = OUTER;
}
else
{
outercontext = NULL;
outer_varno = 0;
}
if (inner_plan)
{
innercontext = deparse_context_for_subplan(inner_name,
(Node *) inner_plan);
inner_varno = INNER;
}
else
{
innercontext = NULL;
inner_varno = 0;
}
context = deparse_context_for_plan(outer_varno, outercontext,
inner_varno, innercontext,
es->rtable); es->rtable);
useprefix = list_length(es->rtable) > 1;
/* Deparse the expression */ /* Deparse the expression */
node = (Node *) make_ands_explicit(qual); node = (Node *) make_ands_explicit(qual);
exprstr = deparse_expression(node, context, (inner_plan != NULL), false); exprstr = deparse_expression(node, context, useprefix, false);
/* And add to str */ /* And add to str */
for (i = 0; i < indent; i++) for (i = 0; i < indent; i++)
...@@ -1154,7 +1098,6 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols, ...@@ -1154,7 +1098,6 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
bool useprefix; bool useprefix;
int keyno; int keyno;
char *exprstr; char *exprstr;
Relids varnos;
int i; int i;
if (nkeys <= 0) if (nkeys <= 0)
...@@ -1164,33 +1107,11 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols, ...@@ -1164,33 +1107,11 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
appendStringInfo(str, " "); appendStringInfo(str, " ");
appendStringInfo(str, " %s: ", qlabel); appendStringInfo(str, " %s: ", qlabel);
/* /* Set up deparsing context */
* In this routine we expect that the plan node's tlist has not been context = deparse_context_for_plan((Node *) outerPlan(sortplan),
* processed by set_plan_references(). Normally, any Vars will contain NULL, /* Sort has no innerPlan */
* valid varnos referencing the actual rtable. But we might instead be es->rtable);
* looking at a dummy tlist generated by prepunion.c; if there are Vars useprefix = list_length(es->rtable) > 1;
* with zero varno, use the tlist itself to determine their names.
*/
varnos = pull_varnos((Node *) sortplan->targetlist);
if (bms_is_member(0, varnos))
{
Node *outercontext;
outercontext = deparse_context_for_subplan("sort",
(Node *) sortplan);
context = deparse_context_for_plan(0, outercontext,
0, NULL,
es->rtable);
useprefix = false;
}
else
{
context = deparse_context_for_plan(0, NULL,
0, NULL,
es->rtable);
useprefix = list_length(es->rtable) > 1;
}
bms_free(varnos);
for (keyno = 0; keyno < nkeys; keyno++) for (keyno = 0; keyno < nkeys; keyno++)
{ {
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.132 2007/02/22 23:44:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.133 2007/02/23 21:59:44 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -73,6 +73,7 @@ static void set_join_references(Join *join, int rtoffset); ...@@ -73,6 +73,7 @@ static void set_join_references(Join *join, int rtoffset);
static void set_inner_join_references(Plan *inner_plan, static void set_inner_join_references(Plan *inner_plan,
indexed_tlist *outer_itlist); indexed_tlist *outer_itlist);
static void set_upper_references(Plan *plan, int rtoffset); static void set_upper_references(Plan *plan, int rtoffset);
static void set_dummy_tlist_references(Plan *plan, int rtoffset);
static indexed_tlist *build_tlist_index(List *tlist); static indexed_tlist *build_tlist_index(List *tlist);
static Var *search_indexed_tlist_for_var(Var *var, static Var *search_indexed_tlist_for_var(Var *var,
indexed_tlist *itlist, indexed_tlist *itlist,
...@@ -315,8 +316,7 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) ...@@ -315,8 +316,7 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
* executor, we fix it up for possible use by EXPLAIN (not to * executor, we fix it up for possible use by EXPLAIN (not to
* mention ease of debugging --- wrong varnos are very confusing). * mention ease of debugging --- wrong varnos are very confusing).
*/ */
plan->targetlist = set_dummy_tlist_references(plan, rtoffset);
fix_scan_list(plan->targetlist, rtoffset);
/* /*
* Since these plan types don't check quals either, we should not * Since these plan types don't check quals either, we should not
* find any qual expression attached to them. * find any qual expression attached to them.
...@@ -330,11 +330,12 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) ...@@ -330,11 +330,12 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
/* /*
* Like the plan types above, Limit doesn't evaluate its tlist * Like the plan types above, Limit doesn't evaluate its tlist
* or quals. It does have live expressions for limit/offset, * or quals. It does have live expressions for limit/offset,
* however. * however; and those cannot contain subplan variable refs,
* so fix_scan_expr works for them.
*/ */
splan->plan.targetlist = set_dummy_tlist_references(plan, rtoffset);
fix_scan_list(splan->plan.targetlist, rtoffset);
Assert(splan->plan.qual == NIL); Assert(splan->plan.qual == NIL);
splan->limitOffset = splan->limitOffset =
fix_scan_expr(splan->limitOffset, rtoffset); fix_scan_expr(splan->limitOffset, rtoffset);
splan->limitCount = splan->limitCount =
...@@ -375,8 +376,7 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) ...@@ -375,8 +376,7 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
* Append, like Sort et al, doesn't actually evaluate its * Append, like Sort et al, doesn't actually evaluate its
* targetlist or check quals. * targetlist or check quals.
*/ */
splan->plan.targetlist = set_dummy_tlist_references(plan, rtoffset);
fix_scan_list(splan->plan.targetlist, rtoffset);
Assert(splan->plan.qual == NIL); Assert(splan->plan.qual == NIL);
foreach(l, splan->appendplans) foreach(l, splan->appendplans)
{ {
...@@ -892,10 +892,7 @@ set_upper_references(Plan *plan, int rtoffset) ...@@ -892,10 +892,7 @@ set_upper_references(Plan *plan, int rtoffset)
List *output_targetlist; List *output_targetlist;
ListCell *l; ListCell *l;
if (subplan != NULL) subplan_itlist = build_tlist_index(subplan->targetlist);
subplan_itlist = build_tlist_index(subplan->targetlist);
else
subplan_itlist = build_tlist_index(NIL);
output_targetlist = NIL; output_targetlist = NIL;
foreach(l, plan->targetlist) foreach(l, plan->targetlist)
...@@ -920,6 +917,58 @@ set_upper_references(Plan *plan, int rtoffset) ...@@ -920,6 +917,58 @@ set_upper_references(Plan *plan, int rtoffset)
pfree(subplan_itlist); pfree(subplan_itlist);
} }
/*
* set_dummy_tlist_references
* Replace the targetlist of an upper-level plan node with a simple
* list of OUTER references to its child.
*
* This is used for plan types like Sort and Append that don't evaluate
* their targetlists. Although the executor doesn't care at all what's in
* the tlist, EXPLAIN needs it to be realistic.
*
* Note: we could almost use set_upper_references() here, but it fails for
* Append for lack of a lefttree subplan. Single-purpose code is faster
* anyway.
*/
static void
set_dummy_tlist_references(Plan *plan, int rtoffset)
{
List *output_targetlist;
ListCell *l;
output_targetlist = NIL;
foreach(l, plan->targetlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(l);
Var *oldvar = (Var *) tle->expr;
Var *newvar;
newvar = makeVar(OUTER,
tle->resno,
exprType((Node *) oldvar),
exprTypmod((Node *) oldvar),
0);
if (IsA(oldvar, Var))
{
newvar->varnoold = oldvar->varno + rtoffset;
newvar->varoattno = oldvar->varattno;
}
else
{
newvar->varnoold = 0; /* wasn't ever a plain Var */
newvar->varoattno = 0;
}
tle = flatCopyTargetEntry(tle);
tle->expr = (Expr *) newvar;
output_targetlist = lappend(output_targetlist, tle);
}
plan->targetlist = output_targetlist;
/* We don't touch plan->qual here */
}
/* /*
* build_tlist_index --- build an index data structure for a child tlist * build_tlist_index --- build an index data structure for a child tlist
* *
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.250 2007/02/22 23:44:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.251 2007/02/23 21:59:44 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -90,17 +90,14 @@ typedef struct ...@@ -90,17 +90,14 @@ typedef struct
* *
* The rangetable is the list of actual RTEs from the query tree. * The rangetable is the list of actual RTEs from the query tree.
* *
* For deparsing plan trees, we allow two special RTE entries that are not * For deparsing plan trees, we provide for outer and inner subplan nodes.
* part of the rtable list (partly because they don't have consecutively * The tlists of these nodes are used to resolve OUTER and INNER varnos.
* allocated varnos).
*/ */
typedef struct typedef struct
{ {
List *rtable; /* List of RangeTblEntry nodes */ List *rtable; /* List of RangeTblEntry nodes */
int outer_varno; /* varno for outer_rte */ Plan *outer_plan; /* OUTER subplan, or NULL if none */
RangeTblEntry *outer_rte; /* special RangeTblEntry, or NULL */ Plan *inner_plan; /* INNER subplan, or NULL if none */
int inner_varno; /* varno for inner_rte */
RangeTblEntry *inner_rte; /* special RangeTblEntry, or NULL */
} deparse_namespace; } deparse_namespace;
...@@ -158,8 +155,8 @@ static void get_setop_query(Node *setOp, Query *query, ...@@ -158,8 +155,8 @@ static void get_setop_query(Node *setOp, Query *query,
static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist, static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist,
bool force_colno, bool force_colno,
deparse_context *context); deparse_context *context);
static void get_names_for_var(Var *var, int levelsup, deparse_context *context, static char *get_variable(Var *var, int levelsup, bool showstar,
char **schemaname, char **refname, char **attname); deparse_context *context);
static RangeTblEntry *find_rte_by_refname(const char *refname, static RangeTblEntry *find_rte_by_refname(const char *refname,
deparse_context *context); deparse_context *context);
static const char *get_simple_binary_op_name(OpExpr *expr); static const char *get_simple_binary_op_name(OpExpr *expr);
...@@ -1434,8 +1431,7 @@ deparse_context_for(const char *aliasname, Oid relid) ...@@ -1434,8 +1431,7 @@ deparse_context_for(const char *aliasname, Oid relid)
/* Build one-element rtable */ /* Build one-element rtable */
dpns->rtable = list_make1(rte); dpns->rtable = list_make1(rte);
dpns->outer_varno = dpns->inner_varno = 0; dpns->outer_plan = dpns->inner_plan = NULL;
dpns->outer_rte = dpns->inner_rte = NULL;
/* Return a one-deep namespace stack */ /* Return a one-deep namespace stack */
return list_make1(dpns); return list_make1(dpns);
...@@ -1444,17 +1440,21 @@ deparse_context_for(const char *aliasname, Oid relid) ...@@ -1444,17 +1440,21 @@ deparse_context_for(const char *aliasname, Oid relid)
/* /*
* deparse_context_for_plan - Build deparse context for a plan node * deparse_context_for_plan - Build deparse context for a plan node
* *
* The plan node may contain references to one or two subplans or outer * When deparsing an expression in a Plan tree, we might have to resolve
* join plan nodes. For these, pass the varno used plus a context node * OUTER or INNER references. Pass the plan nodes whose targetlists define
* made with deparse_context_for_subplan. (Pass 0/NULL for unused inputs.) * 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.)
*
* 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).
* *
* The plan's rangetable list must also be passed. We actually prefer to use * 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 subplan inputs are needed
* for Vars that reference expressions computed in subplan target lists. * for Vars that reference expressions computed in subplan target lists.
*/ */
List * List *
deparse_context_for_plan(int outer_varno, Node *outercontext, deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
int inner_varno, Node *innercontext,
List *rtable) List *rtable)
{ {
deparse_namespace *dpns; deparse_namespace *dpns;
...@@ -1462,47 +1462,13 @@ deparse_context_for_plan(int outer_varno, Node *outercontext, ...@@ -1462,47 +1462,13 @@ deparse_context_for_plan(int outer_varno, Node *outercontext,
dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace)); dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
dpns->rtable = rtable; dpns->rtable = rtable;
dpns->outer_varno = outer_varno; dpns->outer_plan = (Plan *) outer_plan;
dpns->outer_rte = (RangeTblEntry *) outercontext; dpns->inner_plan = (Plan *) inner_plan;
dpns->inner_varno = inner_varno;
dpns->inner_rte = (RangeTblEntry *) innercontext;
/* Return a one-deep namespace stack */ /* Return a one-deep namespace stack */
return list_make1(dpns); return list_make1(dpns);
} }
/*
* deparse_context_for_subplan - Build deparse context for a plan node
*
* Helper routine to build one of the inputs for deparse_context_for_plan.
* Pass the name to be used to reference the subplan, plus the Plan node.
* (subplan really ought to be declared as "Plan *", but we use "Node *"
* to avoid having to include plannodes.h in builtins.h.)
*
* The returned node is actually a RangeTblEntry, but we declare it as just
* Node to discourage callers from assuming anything.
*/
Node *
deparse_context_for_subplan(const char *name, Node *subplan)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
/*
* We create an RTE_SPECIAL RangeTblEntry, and store the subplan in its
* funcexpr field. RTE_SPECIAL nodes shouldn't appear in deparse contexts
* otherwise.
*/
rte->rtekind = RTE_SPECIAL;
rte->relid = InvalidOid;
rte->funcexpr = subplan;
rte->alias = NULL;
rte->eref = makeAlias(name, NIL);
rte->inh = false;
rte->inFromCl = true;
return (Node *) rte;
}
/* ---------- /* ----------
* make_ruledef - reconstruct the CREATE RULE command * make_ruledef - reconstruct the CREATE RULE command
* for a given pg_rewrite tuple * for a given pg_rewrite tuple
...@@ -1645,8 +1611,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, ...@@ -1645,8 +1611,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
context.prettyFlags = prettyFlags; context.prettyFlags = prettyFlags;
context.indentLevel = PRETTYINDENT_STD; context.indentLevel = PRETTYINDENT_STD;
dpns.rtable = query->rtable; dpns.rtable = query->rtable;
dpns.outer_varno = dpns.inner_varno = 0; dpns.outer_plan = dpns.inner_plan = NULL;
dpns.outer_rte = dpns.inner_rte = NULL;
get_rule_expr(qual, &context, false); get_rule_expr(qual, &context, false);
} }
...@@ -1789,8 +1754,7 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace, ...@@ -1789,8 +1754,7 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
context.indentLevel = startIndent; context.indentLevel = startIndent;
dpns.rtable = query->rtable; dpns.rtable = query->rtable;
dpns.outer_varno = dpns.inner_varno = 0; dpns.outer_plan = dpns.inner_plan = NULL;
dpns.outer_rte = dpns.inner_rte = NULL;
switch (query->commandType) switch (query->commandType)
{ {
...@@ -2129,38 +2093,7 @@ get_target_list(List *targetList, deparse_context *context, ...@@ -2129,38 +2093,7 @@ get_target_list(List *targetList, deparse_context *context,
*/ */
if (tle->expr && IsA(tle->expr, Var)) if (tle->expr && IsA(tle->expr, Var))
{ {
Var *var = (Var *) (tle->expr); attname = get_variable((Var *) tle->expr, 0, false, context);
char *schemaname;
char *refname;
get_names_for_var(var, 0, context,
&schemaname, &refname, &attname);
if (refname && (context->varprefix || attname == NULL))
{
if (schemaname)
appendStringInfo(buf, "%s.",
quote_identifier(schemaname));
if (strcmp(refname, "*NEW*") == 0)
appendStringInfoString(buf, "new");
else if (strcmp(refname, "*OLD*") == 0)
appendStringInfoString(buf, "old");
else
appendStringInfoString(buf, quote_identifier(refname));
if (attname)
appendStringInfoChar(buf, '.');
}
if (attname)
appendStringInfoString(buf, quote_identifier(attname));
else
{
/*
* In the whole-row Var case, refname is what the default AS
* name would be.
*/
attname = refname;
}
} }
else else
{ {
...@@ -2596,25 +2529,60 @@ get_utility_query_def(Query *query, deparse_context *context) ...@@ -2596,25 +2529,60 @@ get_utility_query_def(Query *query, deparse_context *context)
/* /*
* Get the RTE referenced by a (possibly nonlocal) Var. * push_plan: set up deparse_namespace to recurse into the tlist of a subplan
* *
* The appropriate attribute number is stored into *attno * When expanding an OUTER or INNER reference, we must push new outer/inner
* (do not assume that var->varattno is what to use). * subplans in case the referenced expression itself uses OUTER/INNER. We
* modify the top stack entry in-place to avoid affecting levelsup issues
* (although in a Plan tree there really shouldn't be any).
*
* Caller must save and restore outer_plan and inner_plan around this.
*/
static void
push_plan(deparse_namespace *dpns, Plan *subplan)
{
/*
* We special-case Append to pretend that the first child plan is the
* OUTER referent; otherwise normal.
*/
if (IsA(subplan, Append))
dpns->outer_plan = (Plan *) linitial(((Append *) subplan)->appendplans);
else
dpns->outer_plan = outerPlan(subplan);
/*
* For a SubqueryScan, pretend the subplan is INNER referent. (We don't
* use OUTER because that could someday conflict with the normal meaning.)
*/
if (IsA(subplan, SubqueryScan))
dpns->inner_plan = ((SubqueryScan *) subplan)->subplan;
else
dpns->inner_plan = innerPlan(subplan);
}
/*
* Display a Var appropriately.
* *
* In some cases (currently only when recursing into an unnamed join) * In some cases (currently only when recursing into an unnamed join)
* the Var's varlevelsup has to be interpreted with respect to a context * the Var's varlevelsup has to be interpreted with respect to a context
* above the current one; levelsup indicates the offset. * above the current one; levelsup indicates the offset.
*
* If showstar is TRUE, whole-row Vars are displayed as "foo.*";
* if FALSE, merely as "foo".
*
* Returns the attname of the Var, or NULL if not determinable.
*/ */
static RangeTblEntry * static char *
get_rte_for_var(Var *var, int levelsup, deparse_context *context, get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
AttrNumber *attno)
{ {
StringInfo buf = context->buf;
RangeTblEntry *rte; RangeTblEntry *rte;
AttrNumber attnum;
int netlevelsup; int netlevelsup;
deparse_namespace *dpns; deparse_namespace *dpns;
char *schemaname;
/* default assumption */ char *refname;
*attno = var->varattno; char *attname;
/* Find appropriate nesting depth */ /* Find appropriate nesting depth */
netlevelsup = var->varlevelsup + levelsup; netlevelsup = var->varlevelsup + levelsup;
...@@ -2628,59 +2596,81 @@ get_rte_for_var(Var *var, int levelsup, deparse_context *context, ...@@ -2628,59 +2596,81 @@ get_rte_for_var(Var *var, int levelsup, deparse_context *context,
* Try to find the relevant RTE in this rtable. In a plan tree, it's * Try to find the relevant RTE in this rtable. In a plan tree, it's
* likely that varno is OUTER or INNER, in which case we try to use * likely that varno is OUTER or INNER, in which case we try to use
* varnoold instead. If the Var references an expression computed by a * varnoold instead. If the Var references an expression computed by a
* subplan, varnoold will be 0, and we fall back to looking at the special * subplan, varnoold will be 0, and we must dig down into the subplans.
* subplan RTEs.
*/ */
if (var->varno >= 1 && var->varno <= list_length(dpns->rtable)) if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
{
rte = rt_fetch(var->varno, dpns->rtable); rte = rt_fetch(var->varno, dpns->rtable);
attnum = var->varattno;
}
else if (var->varnoold >= 1 && var->varnoold <= list_length(dpns->rtable)) else if (var->varnoold >= 1 && var->varnoold <= list_length(dpns->rtable))
{ {
rte = rt_fetch(var->varnoold, dpns->rtable); rte = rt_fetch(var->varnoold, dpns->rtable);
*attno = var->varoattno; attnum = var->varoattno;
} }
else if (var->varno == dpns->outer_varno) else if (var->varno == OUTER && dpns->outer_plan)
rte = dpns->outer_rte; {
else if (var->varno == dpns->inner_varno) TargetEntry *tle;
rte = dpns->inner_rte; Plan *save_outer;
else Plan *save_inner;
rte = NULL;
if (rte == NULL)
elog(ERROR, "bogus varno: %d", var->varno);
return rte;
}
tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
/* Assert(netlevelsup == 0);
* Get the schemaname, refname and attname for a (possibly nonlocal) Var. save_outer = dpns->outer_plan;
* save_inner = dpns->inner_plan;
* In some cases (currently only when recursing into an unnamed join) push_plan(dpns, dpns->outer_plan);
* the Var's varlevelsup has to be interpreted with respect to a context
* above the current one; levelsup indicates the offset. /*
* * Force parentheses because our caller probably assumed a Var is a
* schemaname is usually returned as NULL. It will be non-null only if * simple expression.
* use of the unqualified refname would find the wrong RTE. */
* appendStringInfoChar(buf, '(');
* refname will be returned as NULL if the Var references an unnamed join. get_rule_expr((Node *) tle->expr, context, true);
* In this case the Var *must* be displayed without any qualification. appendStringInfoChar(buf, ')');
*
* attname will be returned as NULL if the Var represents a whole tuple dpns->outer_plan = save_outer;
* of the relation. (Typically we'd want to display the Var as "foo.*", dpns->inner_plan = save_inner;
* but it's convenient to return NULL to make it easier for callers to return NULL;
* distinguish this case.) }
*/ else if (var->varno == INNER && dpns->inner_plan)
static void {
get_names_for_var(Var *var, int levelsup, deparse_context *context, TargetEntry *tle;
char **schemaname, char **refname, char **attname) Plan *save_outer;
{ Plan *save_inner;
RangeTblEntry *rte;
AttrNumber attnum; tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
/* Find appropriate RTE */ Assert(netlevelsup == 0);
rte = get_rte_for_var(var, levelsup, context, &attnum); save_outer = dpns->outer_plan;
save_inner = dpns->inner_plan;
push_plan(dpns, dpns->inner_plan);
/* Emit results */ /*
*schemaname = NULL; /* default assumptions */ * Force parentheses because our caller probably assumed a Var is a
*refname = rte->eref->aliasname; * simple expression.
*/
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) tle->expr, context, true);
appendStringInfoChar(buf, ')');
dpns->outer_plan = save_outer;
dpns->inner_plan = save_inner;
return NULL;
}
else
{
elog(ERROR, "bogus varno: %d", var->varno);
return NULL; /* keep compiler quiet */
}
/* Identify names to use */
schemaname = NULL; /* default assumptions */
refname = rte->eref->aliasname;
/* Exceptions occur only if the RTE is alias-less */ /* Exceptions occur only if the RTE is alias-less */
if (rte->alias == NULL) if (rte->alias == NULL)
...@@ -2693,18 +2683,17 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context, ...@@ -2693,18 +2683,17 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context,
* to specify the schemaname to avoid these errors. * to specify the schemaname to avoid these errors.
*/ */
if (find_rte_by_refname(rte->eref->aliasname, context) != rte) if (find_rte_by_refname(rte->eref->aliasname, context) != rte)
*schemaname = schemaname = get_namespace_name(get_rel_namespace(rte->relid));
get_namespace_name(get_rel_namespace(rte->relid));
} }
else if (rte->rtekind == RTE_JOIN) else if (rte->rtekind == RTE_JOIN)
{ {
/* /*
* If it's an unnamed join, look at the expansion of the alias * If it's an unnamed join, look at the expansion of the alias
* variable. If it's a simple reference to one of the input vars * variable. If it's a simple reference to one of the input vars
* then recursively find the name of that var, instead. (This * then recursively print the name of that var, instead. (This
* allows correct decompiling of cases where there are identically * allows correct decompiling of cases where there are identically
* named columns on both sides of the join.) When it's not a * named columns on both sides of the join.) When it's not a
* simple reference, we have to just return the unqualified * simple reference, we have to just print the unqualified
* variable name (this can only happen with columns that were * variable name (this can only happen with columns that were
* merged by USING or NATURAL clauses). * merged by USING or NATURAL clauses).
*/ */
...@@ -2715,63 +2704,55 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context, ...@@ -2715,63 +2704,55 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context,
aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
if (IsA(aliasvar, Var)) if (IsA(aliasvar, Var))
{ {
get_names_for_var(aliasvar, return get_variable(aliasvar, var->varlevelsup + levelsup,
var->varlevelsup + levelsup, context, showstar, context);
schemaname, refname, attname);
return;
} }
} }
/* Unnamed join has neither schemaname nor refname */ /* Unnamed join has neither schemaname nor refname */
*refname = NULL; refname = NULL;
}
else if (rte->rtekind == RTE_SPECIAL)
{
/*
* This case occurs during EXPLAIN when we are looking at a
* deparse context node set up by deparse_context_for_subplan().
* If the subplan tlist provides a name, use it, but usually we'll
* end up with "?columnN?".
*/
List *tlist = ((Plan *) rte->funcexpr)->targetlist;
TargetEntry *tle = get_tle_by_resno(tlist, attnum);
if (tle && tle->resname)
{
*attname = tle->resname;
}
else
{
char buf[32];
snprintf(buf, sizeof(buf), "?column%d?", attnum);
*attname = pstrdup(buf);
}
return;
} }
} }
if (attnum == InvalidAttrNumber) if (attnum == InvalidAttrNumber)
*attname = NULL; attname = NULL;
else else
*attname = get_rte_attribute_name(rte, attnum); attname = get_rte_attribute_name(rte, attnum);
if (refname && (context->varprefix || attname == NULL))
{
if (schemaname)
appendStringInfo(buf, "%s.",
quote_identifier(schemaname));
if (strcmp(refname, "*NEW*") == 0)
appendStringInfoString(buf, "new");
else if (strcmp(refname, "*OLD*") == 0)
appendStringInfoString(buf, "old");
else
appendStringInfoString(buf, quote_identifier(refname));
if (attname || showstar)
appendStringInfoChar(buf, '.');
}
if (attname)
appendStringInfoString(buf, quote_identifier(attname));
else if (showstar)
appendStringInfoChar(buf, '*');
return attname;
} }
/* /*
* Get the name of a field of a Var of type RECORD. * Get the name of a field of an expression of composite type.
* *
* This is fairly straightforward except for the case of a Var of type RECORD.
* Since no actual table or view column is allowed to have type RECORD, such * Since no actual table or view column is allowed to have type RECORD, such
* a Var must refer to a JOIN or FUNCTION RTE or to a subquery output. We * a Var must refer to a JOIN or FUNCTION RTE or to a subquery output. We
* drill down to find the ultimate defining expression and attempt to infer * drill down to find the ultimate defining expression and attempt to infer
* the field name from it. We ereport if we can't determine the name. * the field name from it. We ereport if we can't determine the name.
* *
* levelsup is an extra offset to interpret the Var's varlevelsup correctly. * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
*
* Note: this 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 have a special case for RTE_SPECIAL so that we can
* deal with displaying RECORD-returning functions in subplan targetlists.
*/ */
static const char * static const char *
get_name_for_var_field(Var *var, int fieldno, get_name_for_var_field(Var *var, int fieldno,
...@@ -2779,15 +2760,100 @@ get_name_for_var_field(Var *var, int fieldno, ...@@ -2779,15 +2760,100 @@ get_name_for_var_field(Var *var, int fieldno,
{ {
RangeTblEntry *rte; RangeTblEntry *rte;
AttrNumber attnum; AttrNumber attnum;
int netlevelsup;
deparse_namespace *dpns;
TupleDesc tupleDesc; TupleDesc tupleDesc;
Node *expr; Node *expr;
/* Check my caller didn't mess up */ /*
Assert(IsA(var, Var)); * If it's a Var of type RECORD, we have to find what the Var refers to;
Assert(var->vartype == RECORDOID); * if not, we can use get_expr_result_type. If that fails, we try
* lookup_rowtype_tupdesc, which will probably fail too, but will ereport
* an acceptable message.
*/
if (!IsA(var, Var) ||
var->vartype != RECORDOID)
{
if (get_expr_result_type((Node *) var, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
tupleDesc = lookup_rowtype_tupdesc_copy(exprType((Node *) var),
exprTypmod((Node *) var));
Assert(tupleDesc);
/* Got the tupdesc, so we can extract the field name */
Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
return NameStr(tupleDesc->attrs[fieldno - 1]->attname);
}
/* Find appropriate nesting depth */
netlevelsup = var->varlevelsup + levelsup;
if (netlevelsup >= list_length(context->namespaces))
elog(ERROR, "bogus varlevelsup: %d offset %d",
var->varlevelsup, levelsup);
dpns = (deparse_namespace *) list_nth(context->namespaces,
netlevelsup);
/* Find appropriate RTE */ /*
rte = get_rte_for_var(var, levelsup, context, &attnum); * Try to find the relevant RTE in this rtable. In a plan tree, it's
* likely that varno is OUTER or INNER, in which case we must dig down
* into the subplans. (We can't shortcut with varnoold here, because
* it might reference a SUBQUERY RTE; we have to dig down to the
* SubqueryScan plan level to cope with that. See below.)
*/
if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
{
rte = rt_fetch(var->varno, dpns->rtable);
attnum = var->varattno;
}
else if (var->varno == OUTER && dpns->outer_plan)
{
TargetEntry *tle;
Plan *save_outer;
Plan *save_inner;
const char *result;
tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
Assert(netlevelsup == 0);
save_outer = dpns->outer_plan;
save_inner = dpns->inner_plan;
push_plan(dpns, dpns->outer_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;
}
else if (var->varno == INNER && dpns->inner_plan)
{
TargetEntry *tle;
Plan *save_outer;
Plan *save_inner;
const char *result;
tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
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;
}
else
{
elog(ERROR, "bogus varno: %d", var->varno);
return NULL; /* keep compiler quiet */
}
if (attnum == InvalidAttrNumber) if (attnum == InvalidAttrNumber)
{ {
...@@ -2795,11 +2861,19 @@ get_name_for_var_field(Var *var, int fieldno, ...@@ -2795,11 +2861,19 @@ get_name_for_var_field(Var *var, int fieldno,
return get_rte_attribute_name(rte, fieldno); return get_rte_attribute_name(rte, 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.
*/
expr = (Node *) var; /* default if we can't drill down */ expr = (Node *) var; /* default if we can't drill down */
switch (rte->rtekind) switch (rte->rtekind)
{ {
case RTE_RELATION: case RTE_RELATION:
case RTE_SPECIAL:
case RTE_VALUES: case RTE_VALUES:
/* /*
...@@ -2810,38 +2884,77 @@ get_name_for_var_field(Var *var, int fieldno, ...@@ -2810,38 +2884,77 @@ get_name_for_var_field(Var *var, int fieldno,
break; break;
case RTE_SUBQUERY: case RTE_SUBQUERY:
{ {
/* Subselect-in-FROM: examine sub-select's output expr */ if (rte->subquery)
TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
attnum);
if (ste == NULL || ste->resjunk)
elog(ERROR, "subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
expr = (Node *) ste->expr;
if (IsA(expr, Var))
{ {
/* /* Subselect-in-FROM: examine sub-select's output expr */
* Recurse into the sub-select to see what its Var refers TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
* to. We have to build an additional level of namespace attnum);
* to keep in step with varlevelsup in the subselect.
*/ if (ste == NULL || ste->resjunk)
deparse_namespace mydpns; elog(ERROR, "subquery %s does not have attribute %d",
const char *result; rte->eref->aliasname, attnum);
expr = (Node *) ste->expr;
if (IsA(expr, Var))
{
/*
* Recurse into the sub-select to see what its Var
* refers to. We have to build an additional level of
* namespace to keep in step with varlevelsup in the
* subselect.
*/
deparse_namespace mydpns;
const char *result;
mydpns.rtable = rte->subquery->rtable; mydpns.rtable = rte->subquery->rtable;
mydpns.outer_varno = mydpns.inner_varno = 0; mydpns.outer_plan = mydpns.inner_plan = NULL;
mydpns.outer_rte = mydpns.inner_rte = NULL;
context->namespaces = lcons(&mydpns, context->namespaces); context->namespaces = lcons(&mydpns,
context->namespaces);
result = get_name_for_var_field((Var *) expr, fieldno, result = get_name_for_var_field((Var *) expr, fieldno,
0, context); 0, context);
context->namespaces = list_delete_first(context->namespaces); context->namespaces =
list_delete_first(context->namespaces);
return result;
}
/* else fall through to inspect the expression */
}
else
{
/*
* 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.
*/
TargetEntry *tle;
Plan *save_outer;
Plan *save_inner;
const char *result;
if (!dpns->inner_plan)
elog(ERROR, "failed to find plan for subquery %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; return result;
} }
/* else fall through to inspect the expression */
} }
break; break;
case RTE_JOIN: case RTE_JOIN:
...@@ -2861,40 +2974,6 @@ get_name_for_var_field(Var *var, int fieldno, ...@@ -2861,40 +2974,6 @@ get_name_for_var_field(Var *var, int fieldno,
* its result columns as RECORD, which is not allowed. * its result columns as RECORD, which is not allowed.
*/ */
break; break;
case RTE_SPECIAL:
{
/*
* We are looking at a deparse context node set up by
* deparse_context_for_subplan(). The Var must refer to an
* expression computed by this subplan (or possibly one of its
* inputs), rather than any simple attribute of an RTE entry.
* Look into the subplan's target list to get the referenced
* expression, digging down as far as needed to find something
* that's not a Var, and then pass it to
* get_expr_result_type().
*/
Plan *subplan = (Plan *) rte->funcexpr;
for (;;)
{
TargetEntry *ste;
ste = get_tle_by_resno(subplan->targetlist,
((Var *) expr)->varattno);
if (!ste || !ste->expr)
break;
expr = (Node *) ste->expr;
if (!IsA(expr, Var))
break;
if (((Var *) expr)->varno == INNER)
subplan = innerPlan(subplan);
else
subplan = outerPlan(subplan);
if (!subplan)
break;
}
}
break;
} }
/* /*
...@@ -2906,7 +2985,7 @@ get_name_for_var_field(Var *var, int fieldno, ...@@ -2906,7 +2985,7 @@ get_name_for_var_field(Var *var, int fieldno,
if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr), tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
exprTypmod(expr)); exprTypmod(expr));
Assert(tupleDesc);
/* Got the tupdesc, so we can extract the field name */ /* Got the tupdesc, so we can extract the field name */
Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
return NameStr(tupleDesc->attrs[fieldno - 1]->attname); return NameStr(tupleDesc->attrs[fieldno - 1]->attname);
...@@ -2947,20 +3026,6 @@ find_rte_by_refname(const char *refname, deparse_context *context) ...@@ -2947,20 +3026,6 @@ find_rte_by_refname(const char *refname, deparse_context *context)
result = rte; result = rte;
} }
} }
if (dpns->outer_rte &&
strcmp(dpns->outer_rte->eref->aliasname, refname) == 0)
{
if (result)
return NULL; /* it's ambiguous */
result = dpns->outer_rte;
}
if (dpns->inner_rte &&
strcmp(dpns->inner_rte->eref->aliasname, refname) == 0)
{
if (result)
return NULL; /* it's ambiguous */
result = dpns->inner_rte;
}
if (result) if (result)
break; break;
} }
...@@ -3291,39 +3356,11 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -3291,39 +3356,11 @@ get_rule_expr(Node *node, deparse_context *context,
* expression tree. The only exception is that when the input is a List, * expression tree. The only exception is that when the input is a List,
* we emit the component items comma-separated with no surrounding * we emit the component items comma-separated with no surrounding
* decoration; this is convenient for most callers. * decoration; this is convenient for most callers.
*
* There might be some work left here to support additional node types.
*/ */
switch (nodeTag(node)) switch (nodeTag(node))
{ {
case T_Var: case T_Var:
{ (void) get_variable((Var *) node, 0, true, context);
Var *var = (Var *) node;
char *schemaname;
char *refname;
char *attname;
get_names_for_var(var, 0, context,
&schemaname, &refname, &attname);
if (refname && (context->varprefix || attname == NULL))
{
if (schemaname)
appendStringInfo(buf, "%s.",
quote_identifier(schemaname));
if (strcmp(refname, "*NEW*") == 0)
appendStringInfoString(buf, "new.");
else if (strcmp(refname, "*OLD*") == 0)
appendStringInfoString(buf, "old.");
else
appendStringInfo(buf, "%s.",
quote_identifier(refname));
}
if (attname)
appendStringInfoString(buf, quote_identifier(attname));
else
appendStringInfoString(buf, "*");
}
break; break;
case T_Const: case T_Const:
...@@ -3509,28 +3546,10 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -3509,28 +3546,10 @@ get_rule_expr(Node *node, deparse_context *context,
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
/* /*
* If it's a Var of type RECORD, we have to find what the Var * Get and print the field name.
* refers to; otherwise we can use get_expr_result_type. If
* that fails, we try lookup_rowtype_tupdesc, which will
* probably fail too, but will ereport an acceptable message.
*/ */
if (IsA(arg, Var) && fieldname = get_name_for_var_field((Var *) arg, fno,
((Var *) arg)->vartype == RECORDOID) 0, context);
fieldname = get_name_for_var_field((Var *) arg, fno,
0, context);
else
{
TupleDesc tupdesc;
if (get_expr_result_type(arg, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
tupdesc = lookup_rowtype_tupdesc_copy(exprType(arg),
exprTypmod(arg));
Assert(tupdesc);
/* Got the tupdesc, so we can extract the field name */
Assert(fno >= 1 && fno <= tupdesc->natts);
fieldname = NameStr(tupdesc->attrs[fno - 1]->attname);
}
appendStringInfo(buf, ".%s", quote_identifier(fieldname)); appendStringInfo(buf, ".%s", quote_identifier(fieldname));
} }
break; break;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.288 2007/02/17 00:55:58 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.289 2007/02/23 21:59:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -537,10 +537,8 @@ extern Datum pg_get_serial_sequence(PG_FUNCTION_ARGS); ...@@ -537,10 +537,8 @@ extern Datum pg_get_serial_sequence(PG_FUNCTION_ARGS);
extern char *deparse_expression(Node *expr, List *dpcontext, extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit); bool forceprefix, bool showimplicit);
extern List *deparse_context_for(const char *aliasname, Oid relid); extern List *deparse_context_for(const char *aliasname, Oid relid);
extern List *deparse_context_for_plan(int outer_varno, Node *outercontext, extern List *deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
int inner_varno, Node *innercontext, List *rtable);
List *rtable);
extern Node *deparse_context_for_subplan(const char *name, Node *subplan);
extern const char *quote_identifier(const char *ident); extern const char *quote_identifier(const char *ident);
extern char *quote_qualified_identifier(const char *namespace, extern char *quote_qualified_identifier(const char *namespace,
const char *ident); const char *ident);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment