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.
This commit is contained in:
Tom Lane 2007-02-23 21:59:45 +00:00
parent 9cc2a71c38
commit 655aa5b330
4 changed files with 443 additions and 456 deletions

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* 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,
Plan *outer_plan,
int indent, ExplainState *es);
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);
static void show_upper_qual(List *qual, const char *qlabel,
const char *outer_name, Plan *outer_plan,
const char *inner_name, Plan *inner_plan,
static void show_upper_qual(List *qual, const char *qlabel, Plan *plan,
StringInfo str, int indent, ExplainState *es);
static void show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
const char *qlabel,
@ -725,19 +723,19 @@ explain_outNode(StringInfo str,
show_scan_qual(((IndexScan *) plan)->indexqualorig,
"Index Cond",
((Scan *) plan)->scanrelid,
outer_plan,
outer_plan, NULL,
str, indent, es);
show_scan_qual(plan->qual,
"Filter",
((Scan *) plan)->scanrelid,
outer_plan,
outer_plan, NULL,
str, indent, es);
break;
case T_BitmapIndexScan:
show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
"Index Cond",
((Scan *) plan)->scanrelid,
outer_plan,
outer_plan, NULL,
str, indent, es);
break;
case T_BitmapHeapScan:
@ -745,17 +743,24 @@ explain_outNode(StringInfo str,
show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
"Recheck Cond",
((Scan *) plan)->scanrelid,
outer_plan,
outer_plan, NULL,
str, indent, es);
/* FALL THRU */
case T_SeqScan:
case T_SubqueryScan:
case T_FunctionScan:
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,
"Filter",
((Scan *) plan)->scanrelid,
outer_plan,
((SubqueryScan *) plan)->subplan,
str, indent, es);
break;
case T_TidScan:
@ -771,67 +776,49 @@ explain_outNode(StringInfo str,
show_scan_qual(tidquals,
"TID Cond",
((Scan *) plan)->scanrelid,
outer_plan,
outer_plan, NULL,
str, indent, es);
show_scan_qual(plan->qual,
"Filter",
((Scan *) plan)->scanrelid,
outer_plan,
outer_plan, NULL,
str, indent, es);
}
break;
case T_NestLoop:
show_upper_qual(((NestLoop *) plan)->join.joinqual,
"Join Filter",
"outer", outerPlan(plan),
"inner", innerPlan(plan),
"Join Filter", plan,
str, indent, es);
show_upper_qual(plan->qual,
"Filter",
"outer", outerPlan(plan),
"inner", innerPlan(plan),
"Filter", plan,
str, indent, es);
break;
case T_MergeJoin:
show_upper_qual(((MergeJoin *) plan)->mergeclauses,
"Merge Cond",
"outer", outerPlan(plan),
"inner", innerPlan(plan),
"Merge Cond", plan,
str, indent, es);
show_upper_qual(((MergeJoin *) plan)->join.joinqual,
"Join Filter",
"outer", outerPlan(plan),
"inner", innerPlan(plan),
"Join Filter", plan,
str, indent, es);
show_upper_qual(plan->qual,
"Filter",
"outer", outerPlan(plan),
"inner", innerPlan(plan),
"Filter", plan,
str, indent, es);
break;
case T_HashJoin:
show_upper_qual(((HashJoin *) plan)->hashclauses,
"Hash Cond",
"outer", outerPlan(plan),
"inner", innerPlan(plan),
"Hash Cond", plan,
str, indent, es);
show_upper_qual(((HashJoin *) plan)->join.joinqual,
"Join Filter",
"outer", outerPlan(plan),
"inner", innerPlan(plan),
"Join Filter", plan,
str, indent, es);
show_upper_qual(plan->qual,
"Filter",
"outer", outerPlan(plan),
"inner", innerPlan(plan),
"Filter", plan,
str, indent, es);
break;
case T_Agg:
case T_Group:
show_upper_qual(plan->qual,
"Filter",
"subplan", outerPlan(plan),
"", NULL,
"Filter", plan,
str, indent, es);
break;
case T_Sort:
@ -843,14 +830,10 @@ explain_outNode(StringInfo str,
break;
case T_Result:
show_upper_qual((List *) ((Result *) plan)->resconstantqual,
"One-Time Filter",
"subplan", outerPlan(plan),
"", NULL,
"One-Time Filter", plan,
str, indent, es);
show_upper_qual(plan->qual,
"Filter",
"subplan", outerPlan(plan),
"", NULL,
"Filter", plan,
str, indent, es);
break;
default:
@ -1032,14 +1015,18 @@ 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.
*/
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)
{
Node *outercontext;
List *context;
bool useprefix;
Node *node;
char *exprstr;
int i;
@ -1051,31 +1038,14 @@ show_scan_qual(List *qual, const char *qlabel,
/* Convert AND list to explicit AND */
node = (Node *) make_ands_explicit(qual);
/*
* If we have an outer plan that is referenced by the qual, add it to the
* deparse context. If not, don't (so that we don't force prefixes
* 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,
/* Set up deparsing context */
context = deparse_context_for_plan((Node *) outer_plan,
(Node *) inner_plan,
es->rtable);
useprefix = (outer_plan != NULL || inner_plan != NULL);
/* Deparse the expression */
exprstr = deparse_expression(node, context, (outercontext != NULL), false);
exprstr = deparse_expression(node, context, useprefix, false);
/* And add to str */
for (i = 0; i < indent; i++)
@ -1087,16 +1057,11 @@ show_scan_qual(List *qual, const char *qlabel,
* Show a qualifier expression for an upper-level plan node
*/
static void
show_upper_qual(List *qual, const char *qlabel,
const char *outer_name, Plan *outer_plan,
const char *inner_name, Plan *inner_plan,
show_upper_qual(List *qual, const char *qlabel, Plan *plan,
StringInfo str, int indent, ExplainState *es)
{
List *context;
Node *outercontext;
Node *innercontext;
int outer_varno;
int inner_varno;
bool useprefix;
Node *node;
char *exprstr;
int i;
@ -1105,36 +1070,15 @@ show_upper_qual(List *qual, const char *qlabel,
if (qual == NIL)
return;
/* Generate deparse context */
if (outer_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,
/* Set up deparsing context */
context = deparse_context_for_plan((Node *) outerPlan(plan),
(Node *) innerPlan(plan),
es->rtable);
useprefix = list_length(es->rtable) > 1;
/* Deparse the expression */
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 */
for (i = 0; i < indent; i++)
@ -1154,7 +1098,6 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
bool useprefix;
int keyno;
char *exprstr;
Relids varnos;
int i;
if (nkeys <= 0)
@ -1164,33 +1107,11 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
appendStringInfo(str, " ");
appendStringInfo(str, " %s: ", qlabel);
/*
* In this routine we expect that the plan node's tlist has not been
* processed by set_plan_references(). Normally, any Vars will contain
* valid varnos referencing the actual rtable. But we might instead be
* looking at a dummy tlist generated by prepunion.c; if there are Vars
* 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);
/* Set up deparsing context */
context = deparse_context_for_plan((Node *) outerPlan(sortplan),
NULL, /* Sort has no innerPlan */
es->rtable);
useprefix = list_length(es->rtable) > 1;
for (keyno = 0; keyno < nkeys; keyno++)
{

View File

@ -9,7 +9,7 @@
*
*
* 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);
static void set_inner_join_references(Plan *inner_plan,
indexed_tlist *outer_itlist);
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 Var *search_indexed_tlist_for_var(Var *var,
indexed_tlist *itlist,
@ -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
* mention ease of debugging --- wrong varnos are very confusing).
*/
plan->targetlist =
fix_scan_list(plan->targetlist, rtoffset);
set_dummy_tlist_references(plan, rtoffset);
/*
* Since these plan types don't check quals either, we should not
* find any qual expression attached to them.
@ -330,11 +330,12 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
/*
* Like the plan types above, Limit doesn't evaluate its tlist
* 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 =
fix_scan_list(splan->plan.targetlist, rtoffset);
set_dummy_tlist_references(plan, rtoffset);
Assert(splan->plan.qual == NIL);
splan->limitOffset =
fix_scan_expr(splan->limitOffset, rtoffset);
splan->limitCount =
@ -375,8 +376,7 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
* Append, like Sort et al, doesn't actually evaluate its
* targetlist or check quals.
*/
splan->plan.targetlist =
fix_scan_list(splan->plan.targetlist, rtoffset);
set_dummy_tlist_references(plan, rtoffset);
Assert(splan->plan.qual == NIL);
foreach(l, splan->appendplans)
{
@ -892,10 +892,7 @@ set_upper_references(Plan *plan, int rtoffset)
List *output_targetlist;
ListCell *l;
if (subplan != NULL)
subplan_itlist = build_tlist_index(subplan->targetlist);
else
subplan_itlist = build_tlist_index(NIL);
subplan_itlist = build_tlist_index(subplan->targetlist);
output_targetlist = NIL;
foreach(l, plan->targetlist)
@ -920,6 +917,58 @@ set_upper_references(Plan *plan, int rtoffset)
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
*

View File

@ -9,7 +9,7 @@
*
*
* 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
*
* 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
* part of the rtable list (partly because they don't have consecutively
* allocated varnos).
* 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.
*/
typedef struct
{
List *rtable; /* List of RangeTblEntry nodes */
int outer_varno; /* varno for outer_rte */
RangeTblEntry *outer_rte; /* special RangeTblEntry, or NULL */
int inner_varno; /* varno for inner_rte */
RangeTblEntry *inner_rte; /* special RangeTblEntry, or NULL */
Plan *outer_plan; /* OUTER subplan, or NULL if none */
Plan *inner_plan; /* INNER subplan, or NULL if none */
} deparse_namespace;
@ -158,8 +155,8 @@ static void get_setop_query(Node *setOp, Query *query,
static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist,
bool force_colno,
deparse_context *context);
static void get_names_for_var(Var *var, int levelsup, deparse_context *context,
char **schemaname, char **refname, char **attname);
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);
static const char *get_simple_binary_op_name(OpExpr *expr);
@ -1434,8 +1431,7 @@ deparse_context_for(const char *aliasname, Oid relid)
/* Build one-element rtable */
dpns->rtable = list_make1(rte);
dpns->outer_varno = dpns->inner_varno = 0;
dpns->outer_rte = dpns->inner_rte = NULL;
dpns->outer_plan = dpns->inner_plan = NULL;
/* Return a one-deep namespace stack */
return list_make1(dpns);
@ -1444,17 +1440,21 @@ deparse_context_for(const char *aliasname, Oid relid)
/*
* deparse_context_for_plan - Build deparse context for a plan node
*
* The plan node may contain references to one or two subplans or outer
* join plan nodes. For these, pass the varno used plus a context node
* made with deparse_context_for_subplan. (Pass 0/NULL for unused inputs.)
* 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.)
*
* 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 rangetable to resolve simple Vars, but the subplan inputs are needed
* for Vars that reference expressions computed in subplan target lists.
*/
List *
deparse_context_for_plan(int outer_varno, Node *outercontext,
int inner_varno, Node *innercontext,
deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
List *rtable)
{
deparse_namespace *dpns;
@ -1462,47 +1462,13 @@ deparse_context_for_plan(int outer_varno, Node *outercontext,
dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
dpns->rtable = rtable;
dpns->outer_varno = outer_varno;
dpns->outer_rte = (RangeTblEntry *) outercontext;
dpns->inner_varno = inner_varno;
dpns->inner_rte = (RangeTblEntry *) innercontext;
dpns->outer_plan = (Plan *) outer_plan;
dpns->inner_plan = (Plan *) inner_plan;
/* Return a one-deep namespace stack */
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
* for a given pg_rewrite tuple
@ -1645,8 +1611,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
context.prettyFlags = prettyFlags;
context.indentLevel = PRETTYINDENT_STD;
dpns.rtable = query->rtable;
dpns.outer_varno = dpns.inner_varno = 0;
dpns.outer_rte = dpns.inner_rte = NULL;
dpns.outer_plan = dpns.inner_plan = NULL;
get_rule_expr(qual, &context, false);
}
@ -1789,8 +1754,7 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
context.indentLevel = startIndent;
dpns.rtable = query->rtable;
dpns.outer_varno = dpns.inner_varno = 0;
dpns.outer_rte = dpns.inner_rte = NULL;
dpns.outer_plan = dpns.inner_plan = NULL;
switch (query->commandType)
{
@ -2129,38 +2093,7 @@ get_target_list(List *targetList, deparse_context *context,
*/
if (tle->expr && IsA(tle->expr, Var))
{
Var *var = (Var *) (tle->expr);
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;
}
attname = get_variable((Var *) tle->expr, 0, false, context);
}
else
{
@ -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
* (do not assume that var->varattno is what to use).
* When expanding an OUTER or INNER reference, we must push new outer/inner
* 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)
* the Var's varlevelsup has to be interpreted with respect to a context
* 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 *
get_rte_for_var(Var *var, int levelsup, deparse_context *context,
AttrNumber *attno)
static char *
get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
{
StringInfo buf = context->buf;
RangeTblEntry *rte;
AttrNumber attnum;
int netlevelsup;
deparse_namespace *dpns;
/* default assumption */
*attno = var->varattno;
char *schemaname;
char *refname;
char *attname;
/* Find appropriate nesting depth */
netlevelsup = var->varlevelsup + levelsup;
@ -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
* 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
* subplan, varnoold will be 0, and we fall back to looking at the special
* subplan RTEs.
* subplan, varnoold will be 0, and we must dig down into the subplans.
*/
if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
{
rte = rt_fetch(var->varno, dpns->rtable);
attnum = var->varattno;
}
else if (var->varnoold >= 1 && var->varnoold <= list_length(dpns->rtable))
{
rte = rt_fetch(var->varnoold, dpns->rtable);
*attno = var->varoattno;
attnum = var->varoattno;
}
else if (var->varno == OUTER && dpns->outer_plan)
{
TargetEntry *tle;
Plan *save_outer;
Plan *save_inner;
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);
/*
* Force parentheses because our caller probably assumed a Var is a
* 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 if (var->varno == INNER && dpns->inner_plan)
{
TargetEntry *tle;
Plan *save_outer;
Plan *save_inner;
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);
/*
* Force parentheses because our caller probably assumed a Var is a
* 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 if (var->varno == dpns->outer_varno)
rte = dpns->outer_rte;
else if (var->varno == dpns->inner_varno)
rte = dpns->inner_rte;
else
rte = NULL;
if (rte == NULL)
{
elog(ERROR, "bogus varno: %d", var->varno);
return rte;
}
return NULL; /* keep compiler quiet */
}
/*
* Get the schemaname, refname and attname for a (possibly nonlocal) Var.
*
* In some cases (currently only when recursing into an unnamed join)
* the Var's varlevelsup has to be interpreted with respect to a context
* above the current one; levelsup indicates the offset.
*
* schemaname is usually returned as NULL. It will be non-null only if
* use of the unqualified refname would find the wrong RTE.
*
* refname will be returned as NULL if the Var references an unnamed join.
* In this case the Var *must* be displayed without any qualification.
*
* attname will be returned as NULL if the Var represents a whole tuple
* of the relation. (Typically we'd want to display the Var as "foo.*",
* but it's convenient to return NULL to make it easier for callers to
* distinguish this case.)
*/
static void
get_names_for_var(Var *var, int levelsup, deparse_context *context,
char **schemaname, char **refname, char **attname)
{
RangeTblEntry *rte;
AttrNumber attnum;
/* Find appropriate RTE */
rte = get_rte_for_var(var, levelsup, context, &attnum);
/* Emit results */
*schemaname = NULL; /* default assumptions */
*refname = rte->eref->aliasname;
/* Identify names to use */
schemaname = NULL; /* default assumptions */
refname = rte->eref->aliasname;
/* Exceptions occur only if the RTE is alias-less */
if (rte->alias == NULL)
@ -2693,18 +2683,17 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context,
* to specify the schemaname to avoid these errors.
*/
if (find_rte_by_refname(rte->eref->aliasname, context) != rte)
*schemaname =
get_namespace_name(get_rel_namespace(rte->relid));
schemaname = get_namespace_name(get_rel_namespace(rte->relid));
}
else if (rte->rtekind == RTE_JOIN)
{
/*
* 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
* 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
* 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
* merged by USING or NATURAL clauses).
*/
@ -2715,63 +2704,55 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context,
aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
if (IsA(aliasvar, Var))
{
get_names_for_var(aliasvar,
var->varlevelsup + levelsup, context,
schemaname, refname, attname);
return;
return get_variable(aliasvar, var->varlevelsup + levelsup,
showstar, context);
}
}
/* Unnamed join has neither schemaname nor refname */
*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;
refname = NULL;
}
}
if (attnum == InvalidAttrNumber)
*attname = NULL;
attname = NULL;
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
* 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
* 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.
*
* 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 *
get_name_for_var_field(Var *var, int fieldno,
@ -2779,15 +2760,100 @@ get_name_for_var_field(Var *var, int fieldno,
{
RangeTblEntry *rte;
AttrNumber attnum;
int netlevelsup;
deparse_namespace *dpns;
TupleDesc tupleDesc;
Node *expr;
/* Check my caller didn't mess up */
Assert(IsA(var, Var));
Assert(var->vartype == RECORDOID);
/*
* If it's a Var of type RECORD, we have to find what the Var refers to;
* 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 RTE */
rte = get_rte_for_var(var, levelsup, context, &attnum);
/* 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);
/*
* 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)
{
@ -2795,11 +2861,19 @@ get_name_for_var_field(Var *var, int 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 */
switch (rte->rtekind)
{
case RTE_RELATION:
case RTE_SPECIAL:
case RTE_VALUES:
/*
@ -2810,38 +2884,77 @@ get_name_for_var_field(Var *var, int fieldno,
break;
case RTE_SUBQUERY:
{
/* Subselect-in-FROM: examine sub-select's output expr */
TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
attnum);
if (rte->subquery)
{
/* Subselect-in-FROM: examine sub-select's output expr */
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))
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))
{
/*
* 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.outer_plan = mydpns.inner_plan = NULL;
context->namespaces = lcons(&mydpns,
context->namespaces);
result = get_name_for_var_field((Var *) expr, fieldno,
0, context);
context->namespaces =
list_delete_first(context->namespaces);
return result;
}
/* else fall through to inspect the expression */
}
else
{
/*
* 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.
* 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.
*/
deparse_namespace mydpns;
TargetEntry *tle;
Plan *save_outer;
Plan *save_inner;
const char *result;
mydpns.rtable = rte->subquery->rtable;
mydpns.outer_varno = mydpns.inner_varno = 0;
mydpns.outer_rte = mydpns.inner_rte = NULL;
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);
context->namespaces = lcons(&mydpns, context->namespaces);
result = get_name_for_var_field((Var *) expr, fieldno,
0, context);
context->namespaces = list_delete_first(context->namespaces);
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 fall through to inspect the expression */
}
break;
case RTE_JOIN:
@ -2861,40 +2974,6 @@ get_name_for_var_field(Var *var, int fieldno,
* its result columns as RECORD, which is not allowed.
*/
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,
if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
exprTypmod(expr));
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);
@ -2947,20 +3026,6 @@ find_rte_by_refname(const char *refname, deparse_context *context)
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)
break;
}
@ -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,
* we emit the component items comma-separated with no surrounding
* decoration; this is convenient for most callers.
*
* There might be some work left here to support additional node types.
*/
switch (nodeTag(node))
{
case T_Var:
{
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, "*");
}
(void) get_variable((Var *) node, 0, true, context);
break;
case T_Const:
@ -3509,28 +3546,10 @@ get_rule_expr(Node *node, deparse_context *context,
appendStringInfoChar(buf, ')');
/*
* If it's a Var of type RECORD, we have to find what the Var
* 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.
* Get and print the field name.
*/
if (IsA(arg, Var) &&
((Var *) arg)->vartype == RECORDOID)
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);
}
fieldname = get_name_for_var_field((Var *) arg, fno,
0, context);
appendStringInfo(buf, ".%s", quote_identifier(fieldname));
}
break;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* 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);
extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit);
extern List *deparse_context_for(const char *aliasname, Oid relid);
extern List *deparse_context_for_plan(int outer_varno, Node *outercontext,
int inner_varno, Node *innercontext,
List *rtable);
extern Node *deparse_context_for_subplan(const char *name, Node *subplan);
extern List *deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
List *rtable);
extern const char *quote_identifier(const char *ident);
extern char *quote_qualified_identifier(const char *namespace,
const char *ident);