diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 6a97b01ec3c43cf37b789ad1a214ef0cf20ea51c..835b1ef6ade2af64a0baf51536167eeb6a75a356 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -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++) { diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 5240a7283b2ff41befd4b5cf5f00c3044c3a0fd2..5c11d788e00247d7f3e673962975e1f0b0487a95 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -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 * diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 2f61b4a9fc57faec2d246b67c4f9bfd8573d5dfe..765b3c65c3060f03ed93b04dc7b2a20741f2c1a7 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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 == 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) @@ -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 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) { @@ -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 (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: @@ -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; diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index db4357c17b7bf1e7c7de263aa8a1796c80208f5b..70c2142d8d422b9ace7f08ba1891ba69cb01bf4f 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -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);