diff options
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 410 |
1 files changed, 271 insertions, 139 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 13685a0a0e4..4af1603e7ce 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -49,6 +49,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "nodes/pathnodes.h" #include "optimizer/optimizer.h" #include "parser/parse_agg.h" #include "parser/parse_func.h" @@ -118,6 +119,8 @@ typedef struct bool varprefix; /* true to print prefixes on Vars */ ParseExprKind special_exprkind; /* set only for exprkinds needing special * handling */ + Bitmapset *appendparents; /* if not null, map child Vars of these relids + * back to the parent rel */ } deparse_context; /* @@ -125,13 +128,18 @@ typedef struct * A Var having varlevelsup=N refers to the N'th item (counting from 0) in * the current context's namespaces list. * - * The rangetable is the list of actual RTEs from the query tree, and the - * cte list is the list of actual CTEs. - * + * rtable is the list of actual RTEs from the Query or PlannedStmt. * rtable_names holds the alias name to be used for each RTE (either a C * string, or NULL for nameless RTEs such as unnamed joins). * rtable_columns holds the column alias names to be used for each RTE. * + * subplans is a list of Plan trees for SubPlans and CTEs (it's only used + * in the PlannedStmt case). + * ctes is a list of CommonTableExpr nodes (only used in the Query case). + * appendrels, if not null (it's only used in the PlannedStmt case), is an + * array of AppendRelInfo nodes, indexed by child relid. We use that to map + * child-table Vars to their inheritance parents. + * * In some cases we need to make names of merged JOIN USING columns unique * across the whole query, not only per-RTE. If so, unique_using is true * and using_names is a list of C strings representing names already assigned @@ -139,28 +147,30 @@ typedef struct * * When deparsing plan trees, there is always just a single item in the * deparse_namespace list (since a plan tree never contains Vars with - * varlevelsup > 0). We store the PlanState node that is the immediate + * varlevelsup > 0). We store the Plan node that is the immediate * parent of the expression to be deparsed, as well as a list of that - * PlanState's ancestors. In addition, we store its outer and inner subplan - * state nodes, as well as their plan nodes' targetlists, and the index tlist - * if the current plan node might contain INDEX_VAR Vars. (These fields could - * be derived on-the-fly from the current PlanState, but it seems notationally - * clearer to set them up as separate fields.) + * Plan's ancestors. In addition, we store its outer and inner subplan nodes, + * as well as their targetlists, and the index tlist if the current plan node + * might contain INDEX_VAR Vars. (These fields could be derived on-the-fly + * from the current Plan node, but it seems notationally clearer to set them + * up as separate fields.) */ typedef struct { List *rtable; /* List of RangeTblEntry nodes */ List *rtable_names; /* Parallel list of names for RTEs */ List *rtable_columns; /* Parallel list of deparse_columns structs */ + List *subplans; /* List of Plan trees for SubPlans */ List *ctes; /* List of CommonTableExpr nodes */ + AppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */ /* Workspace for column alias assignment: */ bool unique_using; /* Are we making USING names globally unique */ List *using_names; /* List of assigned names for USING columns */ /* Remaining fields are used only when deparsing a Plan tree: */ - PlanState *planstate; /* immediate parent of current expression */ - List *ancestors; /* ancestors of planstate */ - PlanState *outer_planstate; /* outer subplan state, or NULL if none */ - PlanState *inner_planstate; /* inner subplan state, or NULL if none */ + Plan *plan; /* immediate parent of current expression */ + List *ancestors; /* ancestors of plan */ + Plan *outer_plan; /* outer subnode, or NULL if none */ + Plan *inner_plan; /* inner subnode, or NULL if none */ List *outer_tlist; /* referent for OUTER_VAR Vars */ List *inner_tlist; /* referent for INNER_VAR Vars */ List *index_tlist; /* referent for INDEX_VAR Vars */ @@ -287,6 +297,10 @@ typedef struct int counter; /* Largest addition used so far for name */ } NameHashEntry; +/* Callback signature for resolve_special_varno() */ +typedef void (*rsv_callback) (Node *node, deparse_context *context, + void *callback_arg); + /* ---------- * Global data @@ -357,8 +371,8 @@ static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte, static void flatten_join_using_qual(Node *qual, List **leftvars, List **rightvars); static char *get_rtable_name(int rtindex, deparse_context *context); -static void set_deparse_planstate(deparse_namespace *dpns, PlanState *ps); -static void push_child_plan(deparse_namespace *dpns, PlanState *ps, +static void set_deparse_plan(deparse_namespace *dpns, Plan *plan); +static void push_child_plan(deparse_namespace *dpns, Plan *plan, deparse_namespace *save_dpns); static void pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns); @@ -404,10 +418,9 @@ static void get_rule_windowspec(WindowClause *wc, List *targetList, static char *get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context); static void get_special_variable(Node *node, deparse_context *context, - void *private); + void *callback_arg); static void resolve_special_varno(Node *node, deparse_context *context, - void *private, - void (*callback) (Node *, deparse_context *, void *)); + rsv_callback callback, void *callback_arg); static Node *find_param_referent(Param *param, deparse_context *context, deparse_namespace **dpns_p, ListCell **ancestor_cell_p); static void get_parameter(Param *param, deparse_context *context); @@ -429,7 +442,7 @@ static void get_func_expr(FuncExpr *expr, deparse_context *context, static void get_agg_expr(Aggref *aggref, deparse_context *context, Aggref *original_aggref); static void get_agg_combine_expr(Node *node, deparse_context *context, - void *private); + void *callback_arg); static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context); static void get_coercion_expr(Node *arg, deparse_context *context, Oid resulttype, int32 resulttypmod, @@ -1022,7 +1035,9 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) /* Build two-element rtable */ memset(&dpns, 0, sizeof(dpns)); dpns.rtable = list_make2(oldrte, newrte); + dpns.subplans = NIL; dpns.ctes = NIL; + dpns.appendrels = NULL; set_rtable_names(&dpns, NIL, NULL); set_simple_column_names(&dpns); @@ -1036,6 +1051,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) context.wrapColumn = WRAP_COLUMN_DEFAULT; context.indentLevel = PRETTYINDENT_STD; context.special_exprkind = EXPR_KIND_NONE; + context.appendparents = NULL; get_rule_expr(qual, &context, false); @@ -3238,6 +3254,7 @@ deparse_expression_pretty(Node *expr, List *dpcontext, context.wrapColumn = WRAP_COLUMN_DEFAULT; context.indentLevel = startIndent; context.special_exprkind = EXPR_KIND_NONE; + context.appendparents = NULL; get_rule_expr(expr, &context, showimplicit); @@ -3274,7 +3291,9 @@ deparse_context_for(const char *aliasname, Oid relid) /* Build one-element rtable */ dpns->rtable = list_make1(rte); + dpns->subplans = NIL; dpns->ctes = NIL; + dpns->appendrels = NULL; set_rtable_names(dpns, NIL, NULL); set_simple_column_names(dpns); @@ -3283,29 +3302,50 @@ deparse_context_for(const char *aliasname, Oid relid) } /* - * deparse_context_for_plan_rtable - Build deparse context for a plan's rtable + * deparse_context_for_plan_tree - Build deparse context for a Plan tree * * When deparsing an expression in a Plan tree, we use the plan's rangetable * to resolve names of simple Vars. The initialization of column names for * this is rather expensive if the rangetable is large, and it'll be the same * for every expression in the Plan tree; so we do it just once and re-use * the result of this function for each expression. (Note that the result - * is not usable until set_deparse_context_planstate() is applied to it.) + * is not usable until set_deparse_context_plan() is applied to it.) * - * In addition to the plan's rangetable list, pass the per-RTE alias names + * In addition to the PlannedStmt, pass the per-RTE alias names * assigned by a previous call to select_rtable_names_for_explain. */ List * -deparse_context_for_plan_rtable(List *rtable, List *rtable_names) +deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names) { deparse_namespace *dpns; dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace)); /* Initialize fields that stay the same across the whole plan tree */ - dpns->rtable = rtable; + dpns->rtable = pstmt->rtable; dpns->rtable_names = rtable_names; + dpns->subplans = pstmt->subplans; dpns->ctes = NIL; + if (pstmt->appendRelations) + { + /* Set up the array, indexed by child relid */ + int ntables = list_length(dpns->rtable); + ListCell *lc; + + dpns->appendrels = (AppendRelInfo **) + palloc0((ntables + 1) * sizeof(AppendRelInfo *)); + foreach(lc, pstmt->appendRelations) + { + AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc); + Index crelid = appinfo->child_relid; + + Assert(crelid > 0 && crelid <= ntables); + Assert(dpns->appendrels[crelid] == NULL); + dpns->appendrels[crelid] = appinfo; + } + } + else + dpns->appendrels = NULL; /* don't need it */ /* * Set up column name aliases. We will get rather bogus results for join @@ -3319,11 +3359,11 @@ deparse_context_for_plan_rtable(List *rtable, List *rtable_names) } /* - * set_deparse_context_planstate - Specify Plan node containing expression + * set_deparse_context_plan - Specify Plan node containing expression * * When deparsing an expression in a Plan tree, we might have to resolve * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must - * provide the parent PlanState node. Then OUTER_VAR and INNER_VAR references + * provide the parent Plan node. Then OUTER_VAR and INNER_VAR references * can be resolved by drilling down into the left and right child plans. * Similarly, INDEX_VAR references can be resolved by reference to the * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in @@ -3332,23 +3372,19 @@ deparse_context_for_plan_rtable(List *rtable, List *rtable_names) * for those, we can only deparse the indexqualorig fields, which won't * contain INDEX_VAR Vars.) * - * Note: planstate really ought to be declared as "PlanState *", but we use - * "Node *" to avoid having to include execnodes.h in ruleutils.h. - * - * The ancestors list is a list of the PlanState's parent PlanStates, the - * most-closely-nested first. This is needed to resolve PARAM_EXEC Params. - * Note we assume that all the PlanStates share the same rtable. + * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes, + * the most-closely-nested first. This is needed to resolve PARAM_EXEC + * Params. Note we assume that all the Plan nodes share the same rtable. * * Once this function has been called, deparse_expression() can be called on - * subsidiary expression(s) of the specified PlanState node. To deparse + * subsidiary expression(s) of the specified Plan node. To deparse * expressions of a different Plan node in the same Plan tree, re-call this * function to identify the new parent Plan node. * * The result is the same List passed in; this is a notational convenience. */ List * -set_deparse_context_planstate(List *dpcontext, - Node *planstate, List *ancestors) +set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors) { deparse_namespace *dpns; @@ -3357,7 +3393,7 @@ set_deparse_context_planstate(List *dpcontext, dpns = (deparse_namespace *) linitial(dpcontext); /* Set our attention on the specific plan node passed in */ - set_deparse_planstate(dpns, (PlanState *) planstate); + set_deparse_plan(dpns, plan); dpns->ancestors = ancestors; return dpcontext; @@ -3377,7 +3413,9 @@ select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used) memset(&dpns, 0, sizeof(dpns)); dpns.rtable = rtable; + dpns.subplans = NIL; dpns.ctes = NIL; + dpns.appendrels = NULL; set_rtable_names(&dpns, NIL, rels_used); /* We needn't bother computing column aliases yet */ @@ -3557,7 +3595,9 @@ set_deparse_for_query(deparse_namespace *dpns, Query *query, /* Initialize *dpns and fill rtable/ctes links */ memset(dpns, 0, sizeof(deparse_namespace)); dpns->rtable = query->rtable; + dpns->subplans = NIL; dpns->ctes = query->cteList; + dpns->appendrels = NULL; /* Assign a unique relation alias to each RTE */ set_rtable_names(dpns, parent_namespaces, NULL); @@ -4625,19 +4665,19 @@ get_rtable_name(int rtindex, deparse_context *context) } /* - * set_deparse_planstate: set up deparse_namespace to parse subexpressions - * of a given PlanState node + * set_deparse_plan: set up deparse_namespace to parse subexpressions + * of a given Plan node * - * This sets the planstate, outer_planstate, inner_planstate, outer_tlist, - * inner_tlist, and index_tlist fields. Caller is responsible for adjusting - * the ancestors list if necessary. Note that the rtable and ctes fields do + * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist, + * and index_tlist fields. Caller is responsible for adjusting the ancestors + * list if necessary. Note that the rtable, subplans, and ctes fields do * not need to change when shifting attention to different plan nodes in a * single plan tree. */ static void -set_deparse_planstate(deparse_namespace *dpns, PlanState *ps) +set_deparse_plan(deparse_namespace *dpns, Plan *plan) { - dpns->planstate = ps; + dpns->plan = plan; /* * We special-case Append and MergeAppend to pretend that the first child @@ -4647,17 +4687,17 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps) * first child plan is the OUTER referent; this is to support RETURNING * lists containing references to non-target relations. */ - if (IsA(ps, AppendState)) - dpns->outer_planstate = ((AppendState *) ps)->appendplans[0]; - else if (IsA(ps, MergeAppendState)) - dpns->outer_planstate = ((MergeAppendState *) ps)->mergeplans[0]; - else if (IsA(ps, ModifyTableState)) - dpns->outer_planstate = ((ModifyTableState *) ps)->mt_plans[0]; + if (IsA(plan, Append)) + dpns->outer_plan = linitial(((Append *) plan)->appendplans); + else if (IsA(plan, MergeAppend)) + dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans); + else if (IsA(plan, ModifyTable)) + dpns->outer_plan = linitial(((ModifyTable *) plan)->plans); else - dpns->outer_planstate = outerPlanState(ps); + dpns->outer_plan = outerPlan(plan); - if (dpns->outer_planstate) - dpns->outer_tlist = dpns->outer_planstate->plan->targetlist; + if (dpns->outer_plan) + dpns->outer_tlist = dpns->outer_plan->targetlist; else dpns->outer_tlist = NIL; @@ -4670,29 +4710,30 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps) * to reuse OUTER, it's used for RETURNING in some modify table cases, * although not INSERT .. CONFLICT). */ - if (IsA(ps, SubqueryScanState)) - dpns->inner_planstate = ((SubqueryScanState *) ps)->subplan; - else if (IsA(ps, CteScanState)) - dpns->inner_planstate = ((CteScanState *) ps)->cteplanstate; - else if (IsA(ps, ModifyTableState)) - dpns->inner_planstate = ps; + if (IsA(plan, SubqueryScan)) + dpns->inner_plan = ((SubqueryScan *) plan)->subplan; + else if (IsA(plan, CteScan)) + dpns->inner_plan = list_nth(dpns->subplans, + ((CteScan *) plan)->ctePlanId - 1); + else if (IsA(plan, ModifyTable)) + dpns->inner_plan = plan; else - dpns->inner_planstate = innerPlanState(ps); + dpns->inner_plan = innerPlan(plan); - if (IsA(ps, ModifyTableState)) - dpns->inner_tlist = ((ModifyTableState *) ps)->mt_excludedtlist; - else if (dpns->inner_planstate) - dpns->inner_tlist = dpns->inner_planstate->plan->targetlist; + if (IsA(plan, ModifyTable)) + dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist; + else if (dpns->inner_plan) + dpns->inner_tlist = dpns->inner_plan->targetlist; else dpns->inner_tlist = NIL; /* Set up referent for INDEX_VAR Vars, if needed */ - if (IsA(ps->plan, IndexOnlyScan)) - dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist; - else if (IsA(ps->plan, ForeignScan)) - dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_scan_tlist; - else if (IsA(ps->plan, CustomScan)) - dpns->index_tlist = ((CustomScan *) ps->plan)->custom_scan_tlist; + if (IsA(plan, IndexOnlyScan)) + dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist; + else if (IsA(plan, ForeignScan)) + dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist; + else if (IsA(plan, CustomScan)) + dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist; else dpns->index_tlist = NIL; } @@ -4710,17 +4751,17 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps) * previous state for pop_child_plan. */ static void -push_child_plan(deparse_namespace *dpns, PlanState *ps, +push_child_plan(deparse_namespace *dpns, Plan *plan, deparse_namespace *save_dpns) { /* Save state for restoration later */ *save_dpns = *dpns; /* Link current plan node into ancestors list */ - dpns->ancestors = lcons(dpns->planstate, dpns->ancestors); + dpns->ancestors = lcons(dpns->plan, dpns->ancestors); /* Set attention on selected child */ - set_deparse_planstate(dpns, ps); + set_deparse_plan(dpns, plan); } /* @@ -4760,7 +4801,7 @@ static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell, deparse_namespace *save_dpns) { - PlanState *ps = (PlanState *) lfirst(ancestor_cell); + Plan *plan = (Plan *) lfirst(ancestor_cell); /* Save state for restoration later */ *save_dpns = *dpns; @@ -4771,7 +4812,7 @@ push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell, list_cell_number(dpns->ancestors, ancestor_cell) + 1); /* Set attention on selected ancestor */ - set_deparse_planstate(dpns, ps); + set_deparse_plan(dpns, plan); } /* @@ -4931,6 +4972,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, context.wrapColumn = WRAP_COLUMN_DEFAULT; context.indentLevel = PRETTYINDENT_STD; context.special_exprkind = EXPR_KIND_NONE; + context.appendparents = NULL; set_deparse_for_query(&dpns, query, NIL); @@ -5093,6 +5135,7 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace, context.wrapColumn = wrapColumn; context.indentLevel = startIndent; context.special_exprkind = EXPR_KIND_NONE; + context.appendparents = NULL; set_deparse_for_query(&dpns, query, parentnamespace); @@ -6694,15 +6737,62 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) */ if (var->varno >= 1 && var->varno <= list_length(dpns->rtable)) { - rte = rt_fetch(var->varno, dpns->rtable); - refname = (char *) list_nth(dpns->rtable_names, var->varno - 1); - colinfo = deparse_columns_fetch(var->varno, dpns); - attnum = var->varattno; + Index varno = var->varno; + AttrNumber varattno = var->varattno; + + /* + * We might have been asked to map child Vars to some parent relation. + */ + if (context->appendparents && dpns->appendrels) + { + Index pvarno = varno; + AttrNumber pvarattno = varattno; + AppendRelInfo *appinfo = dpns->appendrels[pvarno]; + bool found = false; + + /* Only map up to inheritance parents, not UNION ALL appendrels */ + while (appinfo && + rt_fetch(appinfo->parent_relid, + dpns->rtable)->rtekind == RTE_RELATION) + { + found = false; + if (pvarattno > 0) /* system columns stay as-is */ + { + if (pvarattno > appinfo->num_child_cols) + break; /* safety check */ + pvarattno = appinfo->parent_colnos[pvarattno - 1]; + if (pvarattno == 0) + break; /* Var is local to child */ + } + + pvarno = appinfo->parent_relid; + found = true; + + /* If the parent is itself a child, continue up. */ + Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable)); + appinfo = dpns->appendrels[pvarno]; + } + + /* + * If we found an ancestral rel, and that rel is included in + * appendparents, print that column not the original one. + */ + if (found && bms_is_member(pvarno, context->appendparents)) + { + varno = pvarno; + varattno = pvarattno; + } + } + + rte = rt_fetch(varno, dpns->rtable); + refname = (char *) list_nth(dpns->rtable_names, varno - 1); + colinfo = deparse_columns_fetch(varno, dpns); + attnum = varattno; } else { - resolve_special_varno((Node *) var, context, NULL, - get_special_variable); + resolve_special_varno((Node *) var, context, + get_special_variable, NULL); return NULL; } @@ -6715,22 +6805,22 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) * no alias. So in that case, drill down to the subplan and print the * contents of the referenced tlist item. This works because in a plan * tree, such Vars can only occur in a SubqueryScan or CteScan node, and - * we'll have set dpns->inner_planstate to reference the child plan node. + * we'll have set dpns->inner_plan to reference the child plan node. */ if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) && attnum > list_length(rte->eref->colnames) && - dpns->inner_planstate) + dpns->inner_plan) { TargetEntry *tle; deparse_namespace save_dpns; - tle = get_tle_by_resno(dpns->inner_tlist, var->varattno); + tle = get_tle_by_resno(dpns->inner_tlist, attnum); if (!tle) elog(ERROR, "invalid attnum %d for relation \"%s\"", - var->varattno, rte->eref->aliasname); + attnum, rte->eref->aliasname); Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_planstate, &save_dpns); + push_child_plan(dpns, dpns->inner_plan, &save_dpns); /* * Force parentheses because our caller probably assumed a Var is a @@ -6829,13 +6919,13 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) * get_rule_expr. */ static void -get_special_variable(Node *node, deparse_context *context, void *private) +get_special_variable(Node *node, deparse_context *context, void *callback_arg) { StringInfo buf = context->buf; /* - * Force parentheses because our caller probably assumed a Var is a simple - * expression. + * For a non-Var referent, force parentheses because our caller probably + * assumed a Var is a simple expression. */ if (!IsA(node, Var)) appendStringInfoChar(buf, '('); @@ -6850,16 +6940,19 @@ get_special_variable(Node *node, deparse_context *context, void *private) * invoke the callback provided. */ static void -resolve_special_varno(Node *node, deparse_context *context, void *private, - void (*callback) (Node *, deparse_context *, void *)) +resolve_special_varno(Node *node, deparse_context *context, + rsv_callback callback, void *callback_arg) { Var *var; deparse_namespace *dpns; + /* This function is recursive, so let's be paranoid. */ + check_stack_depth(); + /* If it's not a Var, invoke the callback. */ if (!IsA(node, Var)) { - callback(node, context, private); + (*callback) (node, context, callback_arg); return; } @@ -6869,20 +6962,37 @@ resolve_special_varno(Node *node, deparse_context *context, void *private, var->varlevelsup); /* - * It's a special RTE, so recurse. + * If varno is special, recurse. */ if (var->varno == OUTER_VAR && dpns->outer_tlist) { TargetEntry *tle; deparse_namespace save_dpns; + Bitmapset *save_appendparents; tle = get_tle_by_resno(dpns->outer_tlist, var->varattno); if (!tle) elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno); - push_child_plan(dpns, dpns->outer_planstate, &save_dpns); - resolve_special_varno((Node *) tle->expr, context, private, callback); + /* + * If we're descending to the first child of an Append or MergeAppend, + * update appendparents. This will affect deparsing of all Vars + * appearing within the eventually-resolved subexpression. + */ + save_appendparents = context->appendparents; + + if (IsA(dpns->plan, Append)) + context->appendparents = bms_union(context->appendparents, + ((Append *) dpns->plan)->apprelids); + else if (IsA(dpns->plan, MergeAppend)) + context->appendparents = bms_union(context->appendparents, + ((MergeAppend *) dpns->plan)->apprelids); + + push_child_plan(dpns, dpns->outer_plan, &save_dpns); + resolve_special_varno((Node *) tle->expr, context, + callback, callback_arg); pop_child_plan(dpns, &save_dpns); + context->appendparents = save_appendparents; return; } else if (var->varno == INNER_VAR && dpns->inner_tlist) @@ -6894,8 +7004,9 @@ resolve_special_varno(Node *node, deparse_context *context, void *private, if (!tle) elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno); - push_child_plan(dpns, dpns->inner_planstate, &save_dpns); - resolve_special_varno((Node *) tle->expr, context, private, callback); + push_child_plan(dpns, dpns->inner_plan, &save_dpns); + resolve_special_varno((Node *) tle->expr, context, + callback, callback_arg); pop_child_plan(dpns, &save_dpns); return; } @@ -6907,14 +7018,15 @@ resolve_special_varno(Node *node, deparse_context *context, void *private, if (!tle) elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno); - resolve_special_varno((Node *) tle->expr, context, private, callback); + resolve_special_varno((Node *) tle->expr, context, + callback, callback_arg); return; } else if (var->varno < 1 || var->varno > list_length(dpns->rtable)) elog(ERROR, "bogus varno: %d", var->varno); /* Not special. Just invoke the callback. */ - callback(node, context, private); + (*callback) (node, context, callback_arg); } /* @@ -7005,6 +7117,10 @@ get_name_for_var_field(Var *var, int fieldno, * Try to find the relevant RTE in this rtable. In a plan tree, it's * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig * down into the subplans, or INDEX_VAR, which is resolved similarly. + * + * Note: unlike get_variable and resolve_special_varno, we need not worry + * about inheritance mapping: a child Var should have the same datatype as + * its parent, and here we're really only interested in the Var's type. */ if (var->varno >= 1 && var->varno <= list_length(dpns->rtable)) { @@ -7022,7 +7138,7 @@ get_name_for_var_field(Var *var, int fieldno, elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno); Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->outer_planstate, &save_dpns); + push_child_plan(dpns, dpns->outer_plan, &save_dpns); result = get_name_for_var_field((Var *) tle->expr, fieldno, levelsup, context); @@ -7041,7 +7157,7 @@ get_name_for_var_field(Var *var, int fieldno, elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno); Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_planstate, &save_dpns); + push_child_plan(dpns, dpns->inner_plan, &save_dpns); result = get_name_for_var_field((Var *) tle->expr, fieldno, levelsup, context); @@ -7151,7 +7267,7 @@ get_name_for_var_field(Var *var, int fieldno, deparse_namespace save_dpns; const char *result; - if (!dpns->inner_planstate) + if (!dpns->inner_plan) elog(ERROR, "failed to find plan for subquery %s", rte->eref->aliasname); tle = get_tle_by_resno(dpns->inner_tlist, attnum); @@ -7159,7 +7275,7 @@ get_name_for_var_field(Var *var, int fieldno, elog(ERROR, "bogus varattno for subquery var: %d", attnum); Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_planstate, &save_dpns); + push_child_plan(dpns, dpns->inner_plan, &save_dpns); result = get_name_for_var_field((Var *) tle->expr, fieldno, levelsup, context); @@ -7269,7 +7385,7 @@ get_name_for_var_field(Var *var, int fieldno, deparse_namespace save_dpns; const char *result; - if (!dpns->inner_planstate) + if (!dpns->inner_plan) elog(ERROR, "failed to find plan for CTE %s", rte->eref->aliasname); tle = get_tle_by_resno(dpns->inner_tlist, attnum); @@ -7277,7 +7393,7 @@ get_name_for_var_field(Var *var, int fieldno, elog(ERROR, "bogus varattno for subquery var: %d", attnum); Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_planstate, &save_dpns); + push_child_plan(dpns, dpns->inner_plan, &save_dpns); result = get_name_for_var_field((Var *) tle->expr, fieldno, levelsup, context); @@ -7318,22 +7434,22 @@ find_param_referent(Param *param, deparse_context *context, /* * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or * SubPlan argument. This will necessarily be in some ancestor of the - * current expression's PlanState. + * current expression's Plan node. */ if (param->paramkind == PARAM_EXEC) { deparse_namespace *dpns; - PlanState *child_ps; + Plan *child_plan; bool in_same_plan_level; ListCell *lc; dpns = (deparse_namespace *) linitial(context->namespaces); - child_ps = dpns->planstate; + child_plan = dpns->plan; in_same_plan_level = true; foreach(lc, dpns->ancestors) { - PlanState *ps = (PlanState *) lfirst(lc); + Node *ancestor = (Node *) lfirst(lc); ListCell *lc2; /* @@ -7341,11 +7457,11 @@ find_param_referent(Param *param, deparse_context *context, * we've crawled up out of a subplan, this couldn't possibly be * the right match. */ - if (IsA(ps, NestLoopState) && - child_ps == innerPlanState(ps) && + if (IsA(ancestor, NestLoop) && + child_plan == innerPlan(ancestor) && in_same_plan_level) { - NestLoop *nl = (NestLoop *) ps->plan; + NestLoop *nl = (NestLoop *) ancestor; foreach(lc2, nl->nestParams) { @@ -7362,19 +7478,14 @@ find_param_referent(Param *param, deparse_context *context, } /* - * Check to see if we're crawling up from a subplan. + * If ancestor is a SubPlan, check the arguments it provides. */ - foreach(lc2, ps->subPlan) + if (IsA(ancestor, SubPlan)) { - SubPlanState *sstate = (SubPlanState *) lfirst(lc2); - SubPlan *subplan = sstate->subplan; + SubPlan *subplan = (SubPlan *) ancestor; ListCell *lc3; ListCell *lc4; - if (child_ps != sstate->planstate) - continue; - - /* Matched subplan, so check its arguments */ forboth(lc3, subplan->parParam, lc4, subplan->args) { int paramid = lfirst_int(lc3); @@ -7382,41 +7493,61 @@ find_param_referent(Param *param, deparse_context *context, if (paramid == param->paramid) { - /* Found a match, so return it */ - *dpns_p = dpns; - *ancestor_cell_p = lc; - return arg; + /* + * Found a match, so return it. But, since Vars in + * the arg are to be evaluated in the surrounding + * context, we have to point to the next ancestor item + * that is *not* a SubPlan. + */ + ListCell *rest; + + for_each_cell(rest, dpns->ancestors, + lnext(dpns->ancestors, lc)) + { + Node *ancestor2 = (Node *) lfirst(rest); + + if (!IsA(ancestor2, SubPlan)) + { + *dpns_p = dpns; + *ancestor_cell_p = rest; + return arg; + } + } + elog(ERROR, "SubPlan cannot be outermost ancestor"); } } - /* Keep looking, but we are emerging from a subplan. */ + /* We have emerged from a subplan. */ in_same_plan_level = false; - break; + + /* SubPlan isn't a kind of Plan, so skip the rest */ + continue; } /* - * Likewise check to see if we're emerging from an initplan. - * Initplans never have any parParams, so no need to search that - * list, but we need to know if we should reset + * Check to see if we're emerging from an initplan of the current + * ancestor plan. Initplans never have any parParams, so no need + * to search that list, but we need to know if we should reset * in_same_plan_level. */ - foreach(lc2, ps->initPlan) + foreach(lc2, ((Plan *) ancestor)->initPlan) { - SubPlanState *sstate = (SubPlanState *) lfirst(lc2); + SubPlan *subplan = castNode(SubPlan, lfirst(lc2)); - if (child_ps != sstate->planstate) + if (child_plan != (Plan *) list_nth(dpns->subplans, + subplan->plan_id - 1)) continue; /* No parameters to be had here. */ - Assert(sstate->subplan->parParam == NIL); + Assert(subplan->parParam == NIL); - /* Keep looking, but we are emerging from an initplan. */ + /* We have emerged from an initplan. */ in_same_plan_level = false; break; } /* No luck, crawl up to next ancestor */ - child_ps = ps; + child_plan = (Plan *) ancestor; } } @@ -9269,11 +9400,12 @@ get_agg_expr(Aggref *aggref, deparse_context *context, */ if (DO_AGGSPLIT_COMBINE(aggref->aggsplit)) { - TargetEntry *tle = linitial_node(TargetEntry, aggref->args); + TargetEntry *tle; Assert(list_length(aggref->args) == 1); - resolve_special_varno((Node *) tle->expr, context, original_aggref, - get_agg_combine_expr); + tle = linitial_node(TargetEntry, aggref->args); + resolve_special_varno((Node *) tle->expr, context, + get_agg_combine_expr, original_aggref); return; } @@ -9358,10 +9490,10 @@ get_agg_expr(Aggref *aggref, deparse_context *context, * Aggref and then calls this. */ static void -get_agg_combine_expr(Node *node, deparse_context *context, void *private) +get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg) { Aggref *aggref; - Aggref *original_aggref = private; + Aggref *original_aggref = callback_arg; if (!IsA(node, Aggref)) elog(ERROR, "combining Aggref does not point to an Aggref"); |