* 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 $
*
*-------------------------------------------------------------------------
*/
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,
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:
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:
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:
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:
/*
* 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;
/* 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++)
* 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;
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++)
bool useprefix;
int keyno;
char *exprstr;
- Relids varnos;
int i;
if (nkeys <= 0)
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++)
{
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*
* 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;
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);
/* 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);
/*
* 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;
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
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);
}
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)
{
*/
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
{
/*
- * 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;
* 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 == 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;
-}
+ 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);
-/*
- * 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;
+ 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);
- /* Find appropriate RTE */
- rte = get_rte_for_var(var, levelsup, context, &attnum);
+ Assert(netlevelsup == 0);
+ save_outer = dpns->outer_plan;
+ save_inner = dpns->inner_plan;
+ push_plan(dpns, dpns->inner_plan);
- /* Emit results */
- *schemaname = NULL; /* default assumptions */
- *refname = rte->eref->aliasname;
+ /*
+ * 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
+ {
+ 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 */
if (rte->alias == NULL)
* 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).
*/
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,
{
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 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)
{
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:
/*
break;
case 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 (rte->subquery)
{
- /*
- * 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;
+ /* 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))
+ {
+ /*
+ * 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_varno = mydpns.inner_varno = 0;
- mydpns.outer_rte = mydpns.inner_rte = NULL;
+ mydpns.rtable = rte->subquery->rtable;
+ mydpns.outer_plan = mydpns.inner_plan = NULL;
- context->namespaces = lcons(&mydpns, context->namespaces);
+ context->namespaces = lcons(&mydpns,
+ context->namespaces);
- result = get_name_for_var_field((Var *) expr, fieldno,
- 0, context);
+ result = get_name_for_var_field((Var *) expr, fieldno,
+ 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;
}
- /* else fall through to inspect the expression */
}
break;
case RTE_JOIN:
* 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;
}
/*
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);
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;
}
* 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:
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;