diff options
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 331 |
1 files changed, 210 insertions, 121 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 08396431384..c8d7d9c21b3 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -104,7 +104,9 @@ typedef struct * 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. + * cte list is the list of actual CTEs. 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). * * When deparsing plan trees, there is always just a single item in the * deparse_namespace list (since a plan tree never contains Vars with @@ -119,6 +121,7 @@ typedef struct typedef struct { List *rtable; /* List of RangeTblEntry nodes */ + List *rtable_names; /* Parallel list of names for RTEs */ List *ctes; /* List of CommonTableExpr nodes */ /* Remaining fields are used only when deparsing a Plan tree: */ PlanState *planstate; /* immediate parent of current expression */ @@ -172,6 +175,11 @@ static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, static int print_function_arguments(StringInfo buf, HeapTuple proctup, bool print_table_args, bool print_defaults); static void print_function_rettype(StringInfo buf, HeapTuple proctup); +static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces, + Bitmapset *rels_used); +static bool refname_is_unique(char *refname, deparse_namespace *dpns, + List *parent_namespaces); +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, deparse_namespace *save_dpns); @@ -212,8 +220,6 @@ static void get_rule_windowspec(WindowClause *wc, List *targetList, deparse_context *context); static char *get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context); -static RangeTblEntry *find_rte_by_refname(const char *refname, - deparse_context *context); 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); @@ -676,7 +682,8 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) oldrte->rtekind = RTE_RELATION; oldrte->relid = trigrec->tgrelid; oldrte->relkind = relkind; - oldrte->eref = makeAlias("old", NIL); + oldrte->alias = makeAlias("old", NIL); + oldrte->eref = oldrte->alias; oldrte->lateral = false; oldrte->inh = false; oldrte->inFromCl = true; @@ -685,7 +692,8 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) newrte->rtekind = RTE_RELATION; newrte->relid = trigrec->tgrelid; newrte->relkind = relkind; - newrte->eref = makeAlias("new", NIL); + newrte->alias = makeAlias("new", NIL); + newrte->eref = newrte->alias; newrte->lateral = false; newrte->inh = false; newrte->inFromCl = true; @@ -694,6 +702,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) memset(&dpns, 0, sizeof(dpns)); dpns.rtable = list_make2(oldrte, newrte); dpns.ctes = NIL; + set_rtable_names(&dpns, NIL, NULL); /* Set up context with one-deep namespace stack */ context.buf = &buf; @@ -2176,7 +2185,8 @@ deparse_context_for(const char *aliasname, Oid relid) rte->rtekind = RTE_RELATION; rte->relid = relid; rte->relkind = RELKIND_RELATION; /* no need for exactness here */ - rte->eref = makeAlias(aliasname, NIL); + rte->alias = makeAlias(aliasname, NIL); + rte->eref = rte->alias; rte->lateral = false; rte->inh = false; rte->inFromCl = true; @@ -2184,6 +2194,7 @@ deparse_context_for(const char *aliasname, Oid relid) /* Build one-element rtable */ dpns->rtable = list_make1(rte); dpns->ctes = NIL; + set_rtable_names(dpns, NIL, NULL); /* Return a one-deep namespace stack */ return list_make1(dpns); @@ -2209,13 +2220,14 @@ deparse_context_for(const char *aliasname, Oid relid) * most-closely-nested first. This is needed to resolve PARAM_EXEC Params. * Note we assume that all the PlanStates share the same rtable. * - * The plan's rangetable list must also be passed. We actually prefer to use - * the rangetable to resolve simple Vars, but the plan inputs are necessary - * for Vars with special varnos. + * The plan's rangetable list must also be passed, along with the per-RTE + * alias names assigned by a previous call to select_rtable_names_for_explain. + * (We use the rangetable to resolve simple Vars, but the plan inputs are + * necessary for Vars with special varnos.) */ List * deparse_context_for_planstate(Node *planstate, List *ancestors, - List *rtable) + List *rtable, List *rtable_names) { deparse_namespace *dpns; @@ -2223,6 +2235,7 @@ deparse_context_for_planstate(Node *planstate, List *ancestors, /* Initialize fields that stay the same across the whole plan tree */ dpns->rtable = rtable; + dpns->rtable_names = rtable_names; dpns->ctes = NIL; /* Set our attention on the specific plan node passed in */ @@ -2234,6 +2247,142 @@ deparse_context_for_planstate(Node *planstate, List *ancestors, } /* + * select_rtable_names_for_explain - Select RTE aliases for EXPLAIN + * + * Determine the aliases we'll use during an EXPLAIN operation. This is + * just a frontend to set_rtable_names. We have to expose the aliases + * to EXPLAIN because EXPLAIN needs to know the right alias names to print. + */ +List * +select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used) +{ + deparse_namespace dpns; + + memset(&dpns, 0, sizeof(dpns)); + dpns.rtable = rtable; + dpns.ctes = NIL; + set_rtable_names(&dpns, NIL, rels_used); + + return dpns.rtable_names; +} + +/* + * set_rtable_names: select RTE aliases to be used in printing variables + * + * We fill in dpns->rtable_names with a list of names that is one-for-one with + * the already-filled dpns->rtable list. Each RTE name is unique among those + * in the new namespace plus any ancestor namespaces listed in + * parent_namespaces. + * + * If rels_used isn't NULL, only RTE indexes listed in it are given aliases. + */ +static void +set_rtable_names(deparse_namespace *dpns, List *parent_namespaces, + Bitmapset *rels_used) +{ + ListCell *lc; + int rtindex = 1; + + dpns->rtable_names = NIL; + foreach(lc, dpns->rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + char *refname; + + if (rels_used && !bms_is_member(rtindex, rels_used)) + { + /* Ignore unreferenced RTE */ + refname = NULL; + } + else if (rte->alias) + { + /* If RTE has a user-defined alias, prefer that */ + refname = rte->alias->aliasname; + } + else if (rte->rtekind == RTE_RELATION) + { + /* Use the current actual name of the relation */ + refname = get_rel_name(rte->relid); + } + else if (rte->rtekind == RTE_JOIN) + { + /* Unnamed join has no refname */ + refname = NULL; + } + else + { + /* Otherwise use whatever the parser assigned */ + refname = rte->eref->aliasname; + } + + /* + * If the selected name isn't unique, append digits to make it so + */ + if (refname && + !refname_is_unique(refname, dpns, parent_namespaces)) + { + char *modname = (char *) palloc(strlen(refname) + 32); + int i = 0; + + do + { + sprintf(modname, "%s_%d", refname, ++i); + } while (!refname_is_unique(modname, dpns, parent_namespaces)); + refname = modname; + } + + dpns->rtable_names = lappend(dpns->rtable_names, refname); + rtindex++; + } +} + +/* + * refname_is_unique: is refname distinct from all already-chosen RTE names? + */ +static bool +refname_is_unique(char *refname, deparse_namespace *dpns, + List *parent_namespaces) +{ + ListCell *lc; + + foreach(lc, dpns->rtable_names) + { + char *oldname = (char *) lfirst(lc); + + if (oldname && strcmp(oldname, refname) == 0) + return false; + } + foreach(lc, parent_namespaces) + { + deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc); + ListCell *lc2; + + foreach(lc2, olddpns->rtable_names) + { + char *oldname = (char *) lfirst(lc2); + + if (oldname && strcmp(oldname, refname) == 0) + return false; + } + } + return true; +} + +/* + * get_rtable_name: convenience function to get a previously assigned RTE alias + * + * The RTE must belong to the topmost namespace level in "context". + */ +static char * +get_rtable_name(int rtindex, deparse_context *context) +{ + deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces); + + Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names)); + return (char *) list_nth(dpns->rtable_names, rtindex - 1); +} + +/* * set_deparse_planstate: set up deparse_namespace to parse subexpressions * of a given PlanState node * @@ -2534,6 +2683,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, memset(&dpns, 0, sizeof(dpns)); dpns.rtable = query->rtable; dpns.ctes = query->cteList; + set_rtable_names(&dpns, NIL, NULL); get_rule_expr(qual, &context, false); } @@ -2680,6 +2830,7 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace, memset(&dpns, 0, sizeof(dpns)); dpns.rtable = query->rtable; dpns.ctes = query->cteList; + set_rtable_names(&dpns, parentnamespace, NULL); switch (query->commandType) { @@ -2899,7 +3050,6 @@ get_select_query_def(Query *query, deparse_context *context, foreach(l, query->rowMarks) { RowMarkClause *rc = (RowMarkClause *) lfirst(l); - RangeTblEntry *rte = rt_fetch(rc->rti, query->rtable); /* don't print implicit clauses */ if (rc->pushedDown) @@ -2912,7 +3062,8 @@ get_select_query_def(Query *query, deparse_context *context, appendContextKeyword(context, " FOR SHARE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); appendStringInfo(buf, " OF %s", - quote_identifier(rte->eref->aliasname)); + quote_identifier(get_rtable_name(rc->rti, + context))); if (rc->noWait) appendStringInfo(buf, " NOWAIT"); } @@ -3854,7 +4005,6 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) AttrNumber attnum; int netlevelsup; deparse_namespace *dpns; - char *schemaname; char *refname; char *attname; @@ -3874,6 +4024,7 @@ 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); attnum = var->varattno; } else if (var->varno == OUTER_VAR && dpns->outer_tlist) @@ -3993,61 +4144,41 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) return NULL; } - /* 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) + /* + * 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 + * 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 print the + * unqualified variable name (this can only happen with columns that were + * merged by USING or NATURAL clauses). + * + * This wouldn't work in decompiling plan trees, because we don't store + * joinaliasvars lists after planning; but a plan tree should never + * contain a join alias variable. + */ + if (rte->rtekind == RTE_JOIN && rte->alias == NULL) { - if (rte->rtekind == RTE_RELATION) - { - /* - * It's possible that use of the bare refname would find another - * more-closely-nested RTE, or be ambiguous, in which case we need - * 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)); - } - else if (rte->rtekind == RTE_JOIN) + if (rte->joinaliasvars == NIL) + elog(ERROR, "cannot decompile join alias var in plan tree"); + if (attnum > 0) { - /* - * 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 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 print the unqualified - * variable name (this can only happen with columns that were - * merged by USING or NATURAL clauses). - * - * This wouldn't work in decompiling plan trees, because we don't - * store joinaliasvars lists after planning; but a plan tree - * should never contain a join alias variable. - */ - if (rte->joinaliasvars == NIL) - elog(ERROR, "cannot decompile join alias var in plan tree"); - if (attnum > 0) - { - Var *aliasvar; + Var *aliasvar; - aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); - if (IsA(aliasvar, Var)) - { - return get_variable(aliasvar, var->varlevelsup + levelsup, - istoplevel, context); - } + aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); + if (IsA(aliasvar, Var)) + { + return get_variable(aliasvar, var->varlevelsup + levelsup, + istoplevel, context); } - - /* - * Unnamed join has neither schemaname nor refname. (Note: since - * it's unnamed, there is no way the user could have referenced it - * to create a whole-row Var for it. So we don't have to cover - * that case below.) - */ - refname = NULL; } + + /* + * Unnamed join has no refname. (Note: since it's unnamed, there is + * no way the user could have referenced it to create a whole-row Var + * for it. So we don't have to cover that case below.) + */ + Assert(refname == NULL); } if (attnum == InvalidAttrNumber) @@ -4057,9 +4188,6 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) if (refname && (context->varprefix || attname == NULL)) { - if (schemaname) - appendStringInfo(buf, "%s.", - quote_identifier(schemaname)); appendStringInfoString(buf, quote_identifier(refname)); appendStringInfoChar(buf, '.'); } @@ -4289,6 +4417,7 @@ get_name_for_var_field(Var *var, int fieldno, memset(&mydpns, 0, sizeof(mydpns)); mydpns.rtable = rte->subquery->rtable; mydpns.ctes = rte->subquery->cteList; + set_rtable_names(&mydpns, context->namespaces, NULL); context->namespaces = lcons(&mydpns, context->namespaces); @@ -4406,6 +4535,7 @@ get_name_for_var_field(Var *var, int fieldno, memset(&mydpns, 0, sizeof(mydpns)); mydpns.rtable = ctequery->rtable; mydpns.ctes = ctequery->cteList; + set_rtable_names(&mydpns, context->namespaces, NULL); new_nslist = list_copy_tail(context->namespaces, ctelevelsup); @@ -4467,47 +4597,6 @@ get_name_for_var_field(Var *var, int fieldno, return NameStr(tupleDesc->attrs[fieldno - 1]->attname); } - -/* - * find_rte_by_refname - look up an RTE by refname in a deparse context - * - * Returns NULL if there is no matching RTE or the refname is ambiguous. - * - * NOTE: this code is not really correct since it does not take account of - * the fact that not all the RTEs in a rangetable may be visible from the - * point where a Var reference appears. For the purposes we need, however, - * the only consequence of a false match is that we might stick a schema - * qualifier on a Var that doesn't really need it. So it seems close - * enough. - */ -static RangeTblEntry * -find_rte_by_refname(const char *refname, deparse_context *context) -{ - RangeTblEntry *result = NULL; - ListCell *nslist; - - foreach(nslist, context->namespaces) - { - deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist); - ListCell *rtlist; - - foreach(rtlist, dpns->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtlist); - - if (strcmp(rte->eref->aliasname, refname) == 0) - { - if (result) - return NULL; /* it's ambiguous */ - result = rte; - } - } - if (result) - break; - } - return result; -} - /* * Try to find the referenced expression for a PARAM_EXEC Param that might * reference a parameter supplied by an upper NestLoop or SubPlan plan node. @@ -6649,6 +6738,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) { int varno = ((RangeTblRef *) jtnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, query->rtable); + char *refname = get_rtable_name(varno, context); bool gavealias = false; if (rte->lateral) @@ -6688,32 +6778,31 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) if (rte->alias != NULL) { - appendStringInfo(buf, " %s", - quote_identifier(rte->alias->aliasname)); + /* Always print alias if user provided one */ + appendStringInfo(buf, " %s", quote_identifier(refname)); gavealias = true; } - else if (rte->rtekind == RTE_RELATION && - strcmp(rte->eref->aliasname, get_relation_name(rte->relid)) != 0) + else if (rte->rtekind == RTE_RELATION) { /* - * Apparently the rel has been renamed since the rule was made. - * Emit a fake alias clause so that variable references will still - * work. This is not a 100% solution but should work in most - * reasonable situations. + * No need to print alias if it's same as relation name (this + * would normally be the case, but not if set_rtable_names had to + * resolve a conflict). */ - appendStringInfo(buf, " %s", - quote_identifier(rte->eref->aliasname)); - gavealias = true; + if (strcmp(refname, get_relation_name(rte->relid)) != 0) + { + appendStringInfo(buf, " %s", quote_identifier(refname)); + gavealias = true; + } } else if (rte->rtekind == RTE_FUNCTION) { /* - * For a function RTE, always give an alias. This covers possible + * For a function RTE, always print alias. This covers possible * renaming of the function and/or instability of the * FigureColname rules for things that aren't simple functions. */ - appendStringInfo(buf, " %s", - quote_identifier(rte->eref->aliasname)); + appendStringInfo(buf, " %s", quote_identifier(refname)); gavealias = true; } |