]> granicus.if.org Git - postgresql/commitdiff
Improve ruleutils.c's heuristics for dealing with rangetable aliases.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 21 Sep 2012 23:03:10 +0000 (19:03 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 21 Sep 2012 23:03:10 +0000 (19:03 -0400)
The previous scheme had bugs in some corner cases involving tables that had
been renamed since a view was made.  This could result in dumped views that
failed to reload or reloaded incorrectly, as seen in bug #7553 from Lloyd
Albin, as well as in some pgsql-hackers discussion back in January.  Also,
its behavior for printing EXPLAIN plans was sometimes confusing because of
willingness to use the same alias for multiple RTEs (it was Ashutosh
Bapat's complaint about that aspect that started the January thread).

To fix, ensure that each RTE in the query has a unique unqualified alias,
by modifying the alias if necessary (we add "_" and digits as needed to
create a non-conflicting name).  Then we can just print its variables with
that alias, avoiding the confusing and bug-prone scheme of sometimes
schema-qualifying variable names.  In EXPLAIN, it proves to be expedient to
take the further step of only assigning such aliases to RTEs that are
actually referenced in the query, since the planner has a habit of
generating extra RTEs with the same alias in situations such as
inheritance-tree expansion.

Although this fixes a bug of very long standing, I'm hesitant to back-patch
such a noticeable behavioral change.  My experiments while creating a
regression test convinced me that actually incorrect output (as opposed to
confusing output) occurs only in very narrow cases, which is backed up by
the lack of previous complaints from the field.  So we may be better off
living with it in released branches; and in any case it'd be smart to let
this ripen awhile in HEAD before we consider back-patching it.

12 files changed:
src/backend/commands/explain.c
src/backend/utils/adt/ruleutils.c
src/include/commands/explain.h
src/include/utils/builtins.h
src/test/regress/expected/aggregates.out
src/test/regress/expected/alter_table.out
src/test/regress/expected/create_view.out
src/test/regress/expected/inherit.out
src/test/regress/expected/select_views.out
src/test/regress/expected/select_views_1.out
src/test/regress/expected/with.out
src/test/regress/sql/create_view.sql

index 1e8f618a3476b9e171658900f92532b87b3cde97..33252a8e205abc3726ffb32c5615ef6fa64e28a8 100644 (file)
@@ -51,6 +51,10 @@ static void ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
                                ExplainState *es);
 static double elapsed_time(instr_time *starttime);
+static void ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
+static void ExplainPreScanMemberNodes(List *plans, PlanState **planstates,
+                                                 Bitmapset **rels_used);
+static void ExplainPreScanSubPlans(List *plans, Bitmapset **rels_used);
 static void ExplainNode(PlanState *planstate, List *ancestors,
                        const char *relationship, const char *plan_name,
                        ExplainState *es);
@@ -539,9 +543,13 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 void
 ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
 {
+       Bitmapset  *rels_used = NULL;
+
        Assert(queryDesc->plannedstmt != NULL);
        es->pstmt = queryDesc->plannedstmt;
        es->rtable = queryDesc->plannedstmt->rtable;
+       ExplainPreScanNode(queryDesc->planstate, &rels_used);
+       es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used);
        ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
 }
 
@@ -640,6 +648,132 @@ elapsed_time(instr_time *starttime)
        return INSTR_TIME_GET_DOUBLE(endtime);
 }
 
+/*
+ * ExplainPreScanNode -
+ *       Prescan the planstate tree to identify which RTEs are referenced
+ *
+ * Adds the relid of each referenced RTE to *rels_used.  The result controls
+ * which RTEs are assigned aliases by select_rtable_names_for_explain. This
+ * ensures that we don't confusingly assign un-suffixed aliases to RTEs that
+ * never appear in the EXPLAIN output (such as inheritance parents).
+ */
+static void
+ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
+{
+       Plan       *plan = planstate->plan;
+
+       switch (nodeTag(plan))
+       {
+               case T_SeqScan:
+               case T_IndexScan:
+               case T_IndexOnlyScan:
+               case T_BitmapHeapScan:
+               case T_TidScan:
+               case T_SubqueryScan:
+               case T_FunctionScan:
+               case T_ValuesScan:
+               case T_CteScan:
+               case T_WorkTableScan:
+               case T_ForeignScan:
+                       *rels_used = bms_add_member(*rels_used,
+                                                                               ((Scan *) plan)->scanrelid);
+                       break;
+               case T_ModifyTable:
+                       /* cf ExplainModifyTarget */
+                       *rels_used = bms_add_member(*rels_used,
+                                         linitial_int(((ModifyTable *) plan)->resultRelations));
+                       break;
+               default:
+                       break;
+       }
+
+       /* initPlan-s */
+       if (planstate->initPlan)
+               ExplainPreScanSubPlans(planstate->initPlan, rels_used);
+
+       /* lefttree */
+       if (outerPlanState(planstate))
+               ExplainPreScanNode(outerPlanState(planstate), rels_used);
+
+       /* righttree */
+       if (innerPlanState(planstate))
+               ExplainPreScanNode(innerPlanState(planstate), rels_used);
+
+       /* special child plans */
+       switch (nodeTag(plan))
+       {
+               case T_ModifyTable:
+                       ExplainPreScanMemberNodes(((ModifyTable *) plan)->plans,
+                                                                 ((ModifyTableState *) planstate)->mt_plans,
+                                                                         rels_used);
+                       break;
+               case T_Append:
+                       ExplainPreScanMemberNodes(((Append *) plan)->appendplans,
+                                                                       ((AppendState *) planstate)->appendplans,
+                                                                         rels_used);
+                       break;
+               case T_MergeAppend:
+                       ExplainPreScanMemberNodes(((MergeAppend *) plan)->mergeplans,
+                                                               ((MergeAppendState *) planstate)->mergeplans,
+                                                                         rels_used);
+                       break;
+               case T_BitmapAnd:
+                       ExplainPreScanMemberNodes(((BitmapAnd *) plan)->bitmapplans,
+                                                                ((BitmapAndState *) planstate)->bitmapplans,
+                                                                         rels_used);
+                       break;
+               case T_BitmapOr:
+                       ExplainPreScanMemberNodes(((BitmapOr *) plan)->bitmapplans,
+                                                                 ((BitmapOrState *) planstate)->bitmapplans,
+                                                                         rels_used);
+                       break;
+               case T_SubqueryScan:
+                       ExplainPreScanNode(((SubqueryScanState *) planstate)->subplan,
+                                                          rels_used);
+                       break;
+               default:
+                       break;
+       }
+
+       /* subPlan-s */
+       if (planstate->subPlan)
+               ExplainPreScanSubPlans(planstate->subPlan, rels_used);
+}
+
+/*
+ * Prescan the constituent plans of a ModifyTable, Append, MergeAppend,
+ * BitmapAnd, or BitmapOr node.
+ *
+ * Note: we don't actually need to examine the Plan list members, but
+ * we need the list in order to determine the length of the PlanState array.
+ */
+static void
+ExplainPreScanMemberNodes(List *plans, PlanState **planstates,
+                                                 Bitmapset **rels_used)
+{
+       int                     nplans = list_length(plans);
+       int                     j;
+
+       for (j = 0; j < nplans; j++)
+               ExplainPreScanNode(planstates[j], rels_used);
+}
+
+/*
+ * Prescan a list of SubPlans (or initPlans, which also use SubPlan nodes).
+ */
+static void
+ExplainPreScanSubPlans(List *plans, Bitmapset **rels_used)
+{
+       ListCell   *lst;
+
+       foreach(lst, plans)
+       {
+               SubPlanState *sps = (SubPlanState *) lfirst(lst);
+
+               ExplainPreScanNode(sps->planstate, rels_used);
+       }
+}
+
 /*
  * ExplainNode -
  *       Appends a description of a plan tree to es->str
@@ -1440,7 +1574,8 @@ show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
        /* Set up deparsing context */
        context = deparse_context_for_planstate((Node *) planstate,
                                                                                        ancestors,
-                                                                                       es->rtable);
+                                                                                       es->rtable,
+                                                                                       es->rtable_names);
        useprefix = list_length(es->rtable) > 1;
 
        /* Deparse each result column (we now include resjunk ones) */
@@ -1471,7 +1606,8 @@ show_expression(Node *node, const char *qlabel,
        /* Set up deparsing context */
        context = deparse_context_for_planstate((Node *) planstate,
                                                                                        ancestors,
-                                                                                       es->rtable);
+                                                                                       es->rtable,
+                                                                                       es->rtable_names);
 
        /* Deparse the expression */
        exprstr = deparse_expression(node, context, useprefix, false);
@@ -1573,7 +1709,8 @@ show_sort_keys_common(PlanState *planstate, int nkeys, AttrNumber *keycols,
        /* Set up deparsing context */
        context = deparse_context_for_planstate((Node *) planstate,
                                                                                        ancestors,
-                                                                                       es->rtable);
+                                                                                       es->rtable,
+                                                                                       es->rtable_names);
        useprefix = (list_length(es->rtable) > 1 || es->verbose);
 
        for (keyno = 0; keyno < nkeys; keyno++)
@@ -1813,8 +1950,10 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
        char       *namespace = NULL;
        const char *objecttag = NULL;
        RangeTblEntry *rte;
+       char       *refname;
 
        rte = rt_fetch(rti, es->rtable);
+       refname = (char *) list_nth(es->rtable_names, rti - 1);
 
        switch (nodeTag(plan))
        {
@@ -1887,10 +2026,9 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
                                                         quote_identifier(objectname));
                else if (objectname != NULL)
                        appendStringInfo(es->str, " %s", quote_identifier(objectname));
-               if (objectname == NULL ||
-                       strcmp(rte->eref->aliasname, objectname) != 0)
-                       appendStringInfo(es->str, " %s",
-                                                        quote_identifier(rte->eref->aliasname));
+               if (refname != NULL &&
+                       (objectname == NULL || strcmp(refname, objectname) != 0))
+                       appendStringInfo(es->str, " %s", quote_identifier(refname));
        }
        else
        {
@@ -1898,7 +2036,8 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
                        ExplainPropertyText(objecttag, objectname, es);
                if (namespace != NULL)
                        ExplainPropertyText("Schema", namespace, es);
-               ExplainPropertyText("Alias", rte->eref->aliasname, es);
+               if (refname != NULL)
+                       ExplainPropertyText("Alias", refname, es);
        }
 }
 
index 08396431384a8f8581492304c8c916c41e4b9516..c8d7d9c21b3a4398008160da844e83e8330e8ad4 100644 (file)
@@ -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 */
@@ -2233,6 +2246,142 @@ deparse_context_for_planstate(Node *planstate, List *ancestors,
        return list_make1(dpns);
 }
 
+/*
+ * 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;
                }
 
index c4215da1e329102df3289f9c88a732f51f90503e..4227f4e59c3d07cee8d0fc95660010705ce5e13b 100644 (file)
@@ -37,6 +37,7 @@ typedef struct ExplainState
        /* other states */
        PlannedStmt *pstmt;                     /* top of plan */
        List       *rtable;                     /* range table */
+       List       *rtable_names;       /* alias names for RTEs */
        int                     indent;                 /* current indentation level */
        List       *grouping_stack; /* format-specific grouping state */
 } ExplainState;
index c9c665dae09c08a726c04ed58591d63e3b1b3347..5bc3a75856d8efe53ed50064ae71d15711b2beaf 100644 (file)
@@ -654,7 +654,9 @@ 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_planstate(Node *planstate, List *ancestors,
-                                                         List *rtable);
+                                                         List *rtable, List *rtable_names);
+extern List *select_rtable_names_for_explain(List *rtable,
+                                                               Bitmapset *rels_used);
 extern const char *quote_identifier(const char *ident);
 extern char *quote_qualified_identifier(const char *qualifier,
                                                   const char *ident);
index 6ca73a0ed73fc9c2fa18701f17fa34b7f12db131..7286f1aa446ffc0f3b0d0527daf2a5bbba34713e 100644 (file)
@@ -705,32 +705,32 @@ insert into minmaxtest2 values(15), (16);
 insert into minmaxtest3 values(17), (18);
 explain (costs off)
   select min(f1), max(f1) from minmaxtest;
-                                        QUERY PLAN                                         
--------------------------------------------------------------------------------------------
+                                          QUERY PLAN                                          
+----------------------------------------------------------------------------------------------
  Result
    InitPlan 1 (returns $0)
      ->  Limit
            ->  Merge Append
-                 Sort Key: public.minmaxtest.f1
+                 Sort Key: minmaxtest.f1
                  ->  Index Only Scan using minmaxtesti on minmaxtest
                        Index Cond: (f1 IS NOT NULL)
-                 ->  Index Only Scan using minmaxtest1i on minmaxtest1 minmaxtest
+                 ->  Index Only Scan using minmaxtest1i on minmaxtest1
                        Index Cond: (f1 IS NOT NULL)
-                 ->  Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest
+                 ->  Index Only Scan Backward using minmaxtest2i on minmaxtest2
                        Index Cond: (f1 IS NOT NULL)
-                 ->  Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest
+                 ->  Index Only Scan using minmaxtest3i on minmaxtest3
                        Index Cond: (f1 IS NOT NULL)
    InitPlan 2 (returns $1)
      ->  Limit
            ->  Merge Append
-                 Sort Key: public.minmaxtest.f1
-                 ->  Index Only Scan Backward using minmaxtesti on minmaxtest
+                 Sort Key: minmaxtest_1.f1
+                 ->  Index Only Scan Backward using minmaxtesti on minmaxtest minmaxtest_1
                        Index Cond: (f1 IS NOT NULL)
-                 ->  Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest
+                 ->  Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest1_1
                        Index Cond: (f1 IS NOT NULL)
-                 ->  Index Only Scan using minmaxtest2i on minmaxtest2 minmaxtest
+                 ->  Index Only Scan using minmaxtest2i on minmaxtest2 minmaxtest2_1
                        Index Cond: (f1 IS NOT NULL)
-                 ->  Index Only Scan Backward using minmaxtest3i on minmaxtest3 minmaxtest
+                 ->  Index Only Scan Backward using minmaxtest3i on minmaxtest3 minmaxtest3_1
                        Index Cond: (f1 IS NOT NULL)
 (25 rows)
 
index 453a3894b202873b31dada5727e7e09ae3f2f142..c22d74c7b562e86d6e839b4ab3a87d3cf7a29149 100644 (file)
@@ -391,9 +391,9 @@ explain (costs off) select * from nv_parent where d between '2011-08-01' and '20
    ->  Append
          ->  Seq Scan on nv_parent
                Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
-         ->  Seq Scan on nv_child_2010 nv_parent
+         ->  Seq Scan on nv_child_2010
                Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
-         ->  Seq Scan on nv_child_2011 nv_parent
+         ->  Seq Scan on nv_child_2011
                Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
 (8 rows)
 
@@ -405,9 +405,9 @@ explain (costs off) select * from nv_parent where d between '2011-08-01'::date a
    ->  Append
          ->  Seq Scan on nv_parent
                Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
-         ->  Seq Scan on nv_child_2010 nv_parent
+         ->  Seq Scan on nv_child_2010
                Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
-         ->  Seq Scan on nv_child_2011 nv_parent
+         ->  Seq Scan on nv_child_2011
                Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
 (8 rows)
 
@@ -418,11 +418,11 @@ explain (costs off) select * from nv_parent where d between '2009-08-01'::date a
    ->  Append
          ->  Seq Scan on nv_parent
                Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
-         ->  Seq Scan on nv_child_2010 nv_parent
+         ->  Seq Scan on nv_child_2010
                Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
-         ->  Seq Scan on nv_child_2011 nv_parent
+         ->  Seq Scan on nv_child_2011
                Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
-         ->  Seq Scan on nv_child_2009 nv_parent
+         ->  Seq Scan on nv_child_2009
                Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
 (10 rows)
 
@@ -435,9 +435,9 @@ explain (costs off) select * from nv_parent where d between '2009-08-01'::date a
    ->  Append
          ->  Seq Scan on nv_parent
                Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
-         ->  Seq Scan on nv_child_2010 nv_parent
+         ->  Seq Scan on nv_child_2010
                Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
-         ->  Seq Scan on nv_child_2009 nv_parent
+         ->  Seq Scan on nv_child_2009
                Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
 (8 rows)
 
index cc93854c423084e1e0a1afdc6d9d67f4c22235e8..d37c88234a279cd48286bbaadb7b6f42104a51ef 100644 (file)
@@ -288,8 +288,368 @@ SELECT relname, relkind, reloptions FROM pg_class
  mysecview4 | v       | {security_barrier=false}
 (4 rows)
 
+-- Test view decompilation in the face of renaming conflicts
+CREATE TABLE tt1 (f1 int, f2 int, f3 text);
+CREATE TABLE tx1 (x1 int, x2 int, x3 text);
+CREATE TABLE temp_view_test.tt1 (y1 int, f2 int, f3 text);
+CREATE VIEW aliased_view_1 AS
+  select * from tt1
+    where exists (select 1 from tx1 where tt1.f1 = tx1.x1);
+CREATE VIEW aliased_view_2 AS
+  select * from tt1 a1
+    where exists (select 1 from tx1 where a1.f1 = tx1.x1);
+CREATE VIEW aliased_view_3 AS
+  select * from tt1
+    where exists (select 1 from tx1 a2 where tt1.f1 = a2.x1);
+CREATE VIEW aliased_view_4 AS
+  select * from temp_view_test.tt1
+    where exists (select 1 from tt1 where temp_view_test.tt1.y1 = tt1.f1);
+\d+ aliased_view_1
+          View "testviewschm2.aliased_view_1"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tt1.f1, tt1.f2, tt1.f3
+   FROM tt1
+  WHERE (EXISTS ( SELECT 1
+           FROM tx1
+          WHERE tt1.f1 = tx1.x1));
+
+\d+ aliased_view_2
+          View "testviewschm2.aliased_view_2"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT a1.f1, a1.f2, a1.f3
+   FROM tt1 a1
+  WHERE (EXISTS ( SELECT 1
+           FROM tx1
+          WHERE a1.f1 = tx1.x1));
+
+\d+ aliased_view_3
+          View "testviewschm2.aliased_view_3"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tt1.f1, tt1.f2, tt1.f3
+   FROM tt1
+  WHERE (EXISTS ( SELECT 1
+           FROM tx1 a2
+          WHERE tt1.f1 = a2.x1));
+
+\d+ aliased_view_4
+          View "testviewschm2.aliased_view_4"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ y1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tt1.y1, tt1.f2, tt1.f3
+   FROM temp_view_test.tt1
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1 tt1_1
+          WHERE tt1.y1 = tt1_1.f1));
+
+ALTER TABLE tx1 RENAME TO a1;
+\d+ aliased_view_1
+          View "testviewschm2.aliased_view_1"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tt1.f1, tt1.f2, tt1.f3
+   FROM tt1
+  WHERE (EXISTS ( SELECT 1
+           FROM a1
+          WHERE tt1.f1 = a1.x1));
+
+\d+ aliased_view_2
+          View "testviewschm2.aliased_view_2"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT a1.f1, a1.f2, a1.f3
+   FROM tt1 a1
+  WHERE (EXISTS ( SELECT 1
+           FROM a1 a1_1
+          WHERE a1.f1 = a1_1.x1));
+
+\d+ aliased_view_3
+          View "testviewschm2.aliased_view_3"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tt1.f1, tt1.f2, tt1.f3
+   FROM tt1
+  WHERE (EXISTS ( SELECT 1
+           FROM a1 a2
+          WHERE tt1.f1 = a2.x1));
+
+\d+ aliased_view_4
+          View "testviewschm2.aliased_view_4"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ y1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tt1.y1, tt1.f2, tt1.f3
+   FROM temp_view_test.tt1
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1 tt1_1
+          WHERE tt1.y1 = tt1_1.f1));
+
+ALTER TABLE tt1 RENAME TO a2;
+\d+ aliased_view_1
+          View "testviewschm2.aliased_view_1"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT a2.f1, a2.f2, a2.f3
+   FROM a2
+  WHERE (EXISTS ( SELECT 1
+           FROM a1
+          WHERE a2.f1 = a1.x1));
+
+\d+ aliased_view_2
+          View "testviewschm2.aliased_view_2"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT a1.f1, a1.f2, a1.f3
+   FROM a2 a1
+  WHERE (EXISTS ( SELECT 1
+           FROM a1 a1_1
+          WHERE a1.f1 = a1_1.x1));
+
+\d+ aliased_view_3
+          View "testviewschm2.aliased_view_3"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT a2.f1, a2.f2, a2.f3
+   FROM a2
+  WHERE (EXISTS ( SELECT 1
+           FROM a1 a2_1
+          WHERE a2.f1 = a2_1.x1));
+
+\d+ aliased_view_4
+          View "testviewschm2.aliased_view_4"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ y1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tt1.y1, tt1.f2, tt1.f3
+   FROM temp_view_test.tt1
+  WHERE (EXISTS ( SELECT 1
+           FROM a2
+          WHERE tt1.y1 = a2.f1));
+
+ALTER TABLE a1 RENAME TO tt1;
+\d+ aliased_view_1
+          View "testviewschm2.aliased_view_1"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT a2.f1, a2.f2, a2.f3
+   FROM a2
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1
+          WHERE a2.f1 = tt1.x1));
+
+\d+ aliased_view_2
+          View "testviewschm2.aliased_view_2"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT a1.f1, a1.f2, a1.f3
+   FROM a2 a1
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1
+          WHERE a1.f1 = tt1.x1));
+
+\d+ aliased_view_3
+          View "testviewschm2.aliased_view_3"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT a2.f1, a2.f2, a2.f3
+   FROM a2
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1 a2_1
+          WHERE a2.f1 = a2_1.x1));
+
+\d+ aliased_view_4
+          View "testviewschm2.aliased_view_4"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ y1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tt1.y1, tt1.f2, tt1.f3
+   FROM temp_view_test.tt1
+  WHERE (EXISTS ( SELECT 1
+           FROM a2
+          WHERE tt1.y1 = a2.f1));
+
+ALTER TABLE a2 RENAME TO tx1;
+ALTER TABLE tx1 SET SCHEMA temp_view_test;
+\d+ aliased_view_1
+          View "testviewschm2.aliased_view_1"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tx1.f1, tx1.f2, tx1.f3
+   FROM temp_view_test.tx1
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1
+          WHERE tx1.f1 = tt1.x1));
+
+\d+ aliased_view_2
+          View "testviewschm2.aliased_view_2"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT a1.f1, a1.f2, a1.f3
+   FROM temp_view_test.tx1 a1
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1
+          WHERE a1.f1 = tt1.x1));
+
+\d+ aliased_view_3
+          View "testviewschm2.aliased_view_3"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tx1.f1, tx1.f2, tx1.f3
+   FROM temp_view_test.tx1
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1 a2
+          WHERE tx1.f1 = a2.x1));
+
+\d+ aliased_view_4
+          View "testviewschm2.aliased_view_4"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ y1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tt1.y1, tt1.f2, tt1.f3
+   FROM temp_view_test.tt1
+  WHERE (EXISTS ( SELECT 1
+           FROM temp_view_test.tx1
+          WHERE tt1.y1 = tx1.f1));
+
+ALTER TABLE temp_view_test.tt1 RENAME TO tmp1;
+ALTER TABLE temp_view_test.tmp1 SET SCHEMA testviewschm2;
+ALTER TABLE tmp1 RENAME TO tx1;
+\d+ aliased_view_1
+          View "testviewschm2.aliased_view_1"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tx1.f1, tx1.f2, tx1.f3
+   FROM temp_view_test.tx1
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1
+          WHERE tx1.f1 = tt1.x1));
+
+\d+ aliased_view_2
+          View "testviewschm2.aliased_view_2"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT a1.f1, a1.f2, a1.f3
+   FROM temp_view_test.tx1 a1
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1
+          WHERE a1.f1 = tt1.x1));
+
+\d+ aliased_view_3
+          View "testviewschm2.aliased_view_3"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ f1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tx1.f1, tx1.f2, tx1.f3
+   FROM temp_view_test.tx1
+  WHERE (EXISTS ( SELECT 1
+           FROM tt1 a2
+          WHERE tx1.f1 = a2.x1));
+
+\d+ aliased_view_4
+          View "testviewschm2.aliased_view_4"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ y1     | integer |           | plain    | 
+ f2     | integer |           | plain    | 
+ f3     | text    |           | extended | 
+View definition:
+ SELECT tx1.y1, tx1.f2, tx1.f3
+   FROM tx1
+  WHERE (EXISTS ( SELECT 1
+           FROM temp_view_test.tx1 tx1_1
+          WHERE tx1.y1 = tx1_1.f1));
+
 DROP SCHEMA temp_view_test CASCADE;
-NOTICE:  drop cascades to 22 other objects
+NOTICE:  drop cascades to 27 other objects
 DETAIL:  drop cascades to table temp_view_test.base_table
 drop cascades to view v7_temp
 drop cascades to view v10_temp
@@ -312,8 +672,13 @@ drop cascades to view temp_view_test.v7
 drop cascades to view temp_view_test.v8
 drop cascades to sequence temp_view_test.seq1
 drop cascades to view temp_view_test.v9
+drop cascades to table temp_view_test.tx1
+drop cascades to view aliased_view_1
+drop cascades to view aliased_view_2
+drop cascades to view aliased_view_3
+drop cascades to view aliased_view_4
 DROP SCHEMA testviewschm2 CASCADE;
-NOTICE:  drop cascades to 20 other objects
+NOTICE:  drop cascades to 22 other objects
 DETAIL:  drop cascades to table t1
 drop cascades to view temporal1
 drop cascades to view temporal2
@@ -334,4 +699,6 @@ drop cascades to view mysecview1
 drop cascades to view mysecview2
 drop cascades to view mysecview3
 drop cascades to view mysecview4
+drop cascades to table tt1
+drop cascades to table tx1
 SET search_path to public;
index 25adcd2346118853ef57943bd22d9b80667ca560..906a928b0c0a44ff1dcac5b3b9803cd508ec7533 100644 (file)
@@ -1105,17 +1105,17 @@ analyze patest1;
 analyze patest2;
 explain (costs off)
 select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1;
-                        QUERY PLAN                        
-----------------------------------------------------------
+                    QUERY PLAN                    
+--------------------------------------------------
  Nested Loop
    ->  Limit
          ->  Seq Scan on int4_tbl
    ->  Append
          ->  Index Scan using patest0i on patest0
                Index Cond: (id = int4_tbl.f1)
-         ->  Index Scan using patest1i on patest1 patest0
+         ->  Index Scan using patest1i on patest1
                Index Cond: (id = int4_tbl.f1)
-         ->  Index Scan using patest2i on patest2 patest0
+         ->  Index Scan using patest2i on patest2
                Index Cond: (id = int4_tbl.f1)
 (10 rows)
 
@@ -1130,17 +1130,17 @@ select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1;
 drop index patest2i;
 explain (costs off)
 select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1;
-                        QUERY PLAN                        
-----------------------------------------------------------
+                    QUERY PLAN                    
+--------------------------------------------------
  Nested Loop
    ->  Limit
          ->  Seq Scan on int4_tbl
    ->  Append
          ->  Index Scan using patest0i on patest0
                Index Cond: (id = int4_tbl.f1)
-         ->  Index Scan using patest1i on patest1 patest0
+         ->  Index Scan using patest1i on patest1
                Index Cond: (id = int4_tbl.f1)
-         ->  Seq Scan on patest2 patest0
+         ->  Seq Scan on patest2
                Filter: (int4_tbl.f1 = id)
 (10 rows)
 
@@ -1178,22 +1178,22 @@ insert into matest3 (name) values ('Test 5');
 insert into matest3 (name) values ('Test 6');
 set enable_indexscan = off;  -- force use of seqscan/sort, so no merge
 explain (verbose, costs off) select * from matest0 order by 1-id;
-                                   QUERY PLAN                                    
----------------------------------------------------------------------------------
+                         QUERY PLAN                         
+------------------------------------------------------------
  Sort
-   Output: public.matest0.id, public.matest0.name, ((1 - public.matest0.id))
-   Sort Key: ((1 - public.matest0.id))
+   Output: matest0.id, matest0.name, ((1 - matest0.id))
+   Sort Key: ((1 - matest0.id))
    ->  Result
-         Output: public.matest0.id, public.matest0.name, (1 - public.matest0.id)
+         Output: matest0.id, matest0.name, (1 - matest0.id)
          ->  Append
                ->  Seq Scan on public.matest0
-                     Output: public.matest0.id, public.matest0.name
-               ->  Seq Scan on public.matest1 matest0
-                     Output: public.matest0.id, public.matest0.name
-               ->  Seq Scan on public.matest2 matest0
-                     Output: public.matest0.id, public.matest0.name
-               ->  Seq Scan on public.matest3 matest0
-                     Output: public.matest0.id, public.matest0.name
+                     Output: matest0.id, matest0.name
+               ->  Seq Scan on public.matest1
+                     Output: matest1.id, matest1.name
+               ->  Seq Scan on public.matest2
+                     Output: matest2.id, matest2.name
+               ->  Seq Scan on public.matest3
+                     Output: matest3.id, matest3.name
 (14 rows)
 
 select * from matest0 order by 1-id;
@@ -1210,23 +1210,23 @@ select * from matest0 order by 1-id;
 reset enable_indexscan;
 set enable_seqscan = off;  -- plan with fewest seqscans should be merge
 explain (verbose, costs off) select * from matest0 order by 1-id;
-                                         QUERY PLAN                                          
----------------------------------------------------------------------------------------------
+                               QUERY PLAN                               
+------------------------------------------------------------------------
  Result
-   Output: public.matest0.id, public.matest0.name, ((1 - public.matest0.id))
+   Output: matest0.id, matest0.name, ((1 - matest0.id))
    ->  Merge Append
-         Sort Key: ((1 - public.matest0.id))
+         Sort Key: ((1 - matest0.id))
          ->  Index Scan using matest0i on public.matest0
-               Output: public.matest0.id, public.matest0.name, (1 - public.matest0.id)
-         ->  Index Scan using matest1i on public.matest1 matest0
-               Output: public.matest0.id, public.matest0.name, (1 - public.matest0.id)
+               Output: matest0.id, matest0.name, (1 - matest0.id)
+         ->  Index Scan using matest1i on public.matest1
+               Output: matest1.id, matest1.name, (1 - matest1.id)
          ->  Sort
-               Output: public.matest0.id, public.matest0.name, ((1 - public.matest0.id))
-               Sort Key: ((1 - public.matest0.id))
-               ->  Seq Scan on public.matest2 matest0
-                     Output: public.matest0.id, public.matest0.name, (1 - public.matest0.id)
-         ->  Index Scan using matest3i on public.matest3 matest0
-               Output: public.matest0.id, public.matest0.name, (1 - public.matest0.id)
+               Output: matest2.id, matest2.name, ((1 - matest2.id))
+               Sort Key: ((1 - matest2.id))
+               ->  Seq Scan on public.matest2
+                     Output: matest2.id, matest2.name, (1 - matest2.id)
+         ->  Index Scan using matest3i on public.matest3
+               Output: matest3.id, matest3.name, (1 - matest3.id)
 (15 rows)
 
 select * from matest0 order by 1-id;
@@ -1258,15 +1258,15 @@ SELECT thousand, tenthous FROM tenk1
 UNION ALL
 SELECT thousand, thousand FROM tenk1
 ORDER BY thousand, tenthous;
-                              QUERY PLAN                               
------------------------------------------------------------------------
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
  Result
    ->  Merge Append
-         Sort Key: public.tenk1.thousand, public.tenk1.tenthous
+         Sort Key: tenk1.thousand, tenk1.tenthous
          ->  Index Only Scan using tenk1_thous_tenthous on tenk1
          ->  Sort
-               Sort Key: public.tenk1.thousand, public.tenk1.thousand
-               ->  Index Only Scan using tenk1_thous_tenthous on tenk1
+               Sort Key: tenk1_1.thousand, tenk1_1.thousand
+               ->  Index Only Scan using tenk1_thous_tenthous on tenk1 tenk1_1
 (7 rows)
 
 explain (costs off)
@@ -1274,15 +1274,15 @@ SELECT thousand, tenthous, thousand+tenthous AS x FROM tenk1
 UNION ALL
 SELECT 42, 42, hundred FROM tenk1
 ORDER BY thousand, tenthous;
-                           QUERY PLAN                            
------------------------------------------------------------------
+                               QUERY PLAN                               
+------------------------------------------------------------------------
  Result
    ->  Merge Append
-         Sort Key: public.tenk1.thousand, public.tenk1.tenthous
+         Sort Key: tenk1.thousand, tenk1.tenthous
          ->  Index Only Scan using tenk1_thous_tenthous on tenk1
          ->  Sort
                Sort Key: (42), (42)
-               ->  Index Only Scan using tenk1_hundred on tenk1
+               ->  Index Only Scan using tenk1_hundred on tenk1 tenk1_1
 (7 rows)
 
 explain (costs off)
@@ -1290,15 +1290,15 @@ SELECT thousand, tenthous FROM tenk1
 UNION ALL
 SELECT thousand, random()::integer FROM tenk1
 ORDER BY thousand, tenthous;
-                              QUERY PLAN                               
------------------------------------------------------------------------
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
  Result
    ->  Merge Append
-         Sort Key: public.tenk1.thousand, public.tenk1.tenthous
+         Sort Key: tenk1.thousand, tenk1.tenthous
          ->  Index Only Scan using tenk1_thous_tenthous on tenk1
          ->  Sort
-               Sort Key: public.tenk1.thousand, ((random())::integer)
-               ->  Index Only Scan using tenk1_thous_tenthous on tenk1
+               Sort Key: tenk1_1.thousand, ((random())::integer)
+               ->  Index Only Scan using tenk1_thous_tenthous on tenk1 tenk1_1
 (7 rows)
 
 -- Check min/max aggregate optimization
index e4eba1ae36c8715d2f414db413feb68e71c1c575..24bbff9d2e4727529d470454f012db0f9a774776 100644 (file)
@@ -1421,10 +1421,10 @@ EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_normal
          ->  Subquery Scan on l
                Filter: f_leak(l.cnum)
                ->  Hash Join
-                     Hash Cond: (r.cid = l.cid)
-                     ->  Seq Scan on credit_card r
+                     Hash Cond: (r_1.cid = l_1.cid)
+                     ->  Seq Scan on credit_card r_1
                      ->  Hash
-                           ->  Seq Scan on customer l
+                           ->  Seq Scan on customer l_1
                                  Filter: (name = ("current_user"())::text)
 (13 rows)
 
@@ -1452,8 +1452,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_secure
                Filter: ((ymd >= '10-01-2011'::date) AND (ymd < '11-01-2011'::date))
          ->  Materialize
                ->  Hash Join
-                     Hash Cond: (r.cid = l.cid)
-                     ->  Seq Scan on credit_card r
+                     Hash Cond: (r_1.cid = l.cid)
+                     ->  Seq Scan on credit_card r_1
                      ->  Hash
                            ->  Seq Scan on customer l
                                  Filter: (name = ("current_user"())::text)
index 94b439825c49b25651a7722f301d203f9d6c958f..ec6e938cb1d3a30d56bc8b0ceeafd0a6f8d6fe43 100644 (file)
@@ -1421,10 +1421,10 @@ EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_normal
          ->  Subquery Scan on l
                Filter: f_leak(l.cnum)
                ->  Hash Join
-                     Hash Cond: (r.cid = l.cid)
-                     ->  Seq Scan on credit_card r
+                     Hash Cond: (r_1.cid = l_1.cid)
+                     ->  Seq Scan on credit_card r_1
                      ->  Hash
-                           ->  Seq Scan on customer l
+                           ->  Seq Scan on customer l_1
                                  Filter: (name = ("current_user"())::text)
 (13 rows)
 
@@ -1452,8 +1452,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_secure
                Filter: ((ymd >= '10-01-2011'::date) AND (ymd < '11-01-2011'::date))
          ->  Materialize
                ->  Hash Join
-                     Hash Cond: (r.cid = l.cid)
-                     ->  Seq Scan on credit_card r
+                     Hash Cond: (r_1.cid = l.cid)
+                     ->  Seq Scan on credit_card r_1
                      ->  Hash
                            ->  Seq Scan on customer l
                                  Filter: (name = ("current_user"())::text)
index 38cfb8c7276c9310a60375f0f70d56b8c14eb970..671f293b68de95617b6f953573a31fda3fde176f 100644 (file)
@@ -2006,8 +2006,8 @@ SELECT * FROM parent;
 EXPLAIN (VERBOSE, COSTS OFF)
 WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 )
 DELETE FROM a USING wcte WHERE aa = q2;
-                    QUERY PLAN                    
---------------------------------------------------
+                   QUERY PLAN                   
+------------------------------------------------
  Delete on public.a
    CTE wcte
      ->  Insert on public.int8_tbl
@@ -2015,31 +2015,31 @@ DELETE FROM a USING wcte WHERE aa = q2;
            ->  Result
                  Output: 42::bigint, 47::bigint
    ->  Nested Loop
-         Output: public.a.ctid, wcte.*
-         Join Filter: (public.a.aa = wcte.q2)
+         Output: a.ctid, wcte.*
+         Join Filter: (a.aa = wcte.q2)
          ->  Seq Scan on public.a
-               Output: public.a.ctid, public.a.aa
+               Output: a.ctid, a.aa
          ->  CTE Scan on wcte
                Output: wcte.*, wcte.q2
    ->  Nested Loop
-         Output: public.a.ctid, wcte.*
-         Join Filter: (public.a.aa = wcte.q2)
-         ->  Seq Scan on public.b a
-               Output: public.a.ctid, public.a.aa
+         Output: b.ctid, wcte.*
+         Join Filter: (b.aa = wcte.q2)
+         ->  Seq Scan on public.b
+               Output: b.ctid, b.aa
          ->  CTE Scan on wcte
                Output: wcte.*, wcte.q2
    ->  Nested Loop
-         Output: public.a.ctid, wcte.*
-         Join Filter: (public.a.aa = wcte.q2)
-         ->  Seq Scan on public.c a
-               Output: public.a.ctid, public.a.aa
+         Output: c.ctid, wcte.*
+         Join Filter: (c.aa = wcte.q2)
+         ->  Seq Scan on public.c
+               Output: c.ctid, c.aa
          ->  CTE Scan on wcte
                Output: wcte.*, wcte.q2
    ->  Nested Loop
-         Output: public.a.ctid, wcte.*
-         Join Filter: (public.a.aa = wcte.q2)
-         ->  Seq Scan on public.d a
-               Output: public.a.ctid, public.a.aa
+         Output: d.ctid, wcte.*
+         Join Filter: (d.aa = wcte.q2)
+         ->  Seq Scan on public.d
+               Output: d.ctid, d.aa
          ->  CTE Scan on wcte
                Output: wcte.*, wcte.q2
 (34 rows)
index 48d8d22d1b8e4e997f1bd236b75a2b7773a6ad7f..07145911e6be500bfa8d7b99d06a7fd3819db79f 100644 (file)
@@ -224,6 +224,68 @@ SELECT relname, relkind, reloptions FROM pg_class
                      'mysecview3'::regclass, 'mysecview4'::regclass)
        ORDER BY relname;
 
+-- Test view decompilation in the face of renaming conflicts
+
+CREATE TABLE tt1 (f1 int, f2 int, f3 text);
+CREATE TABLE tx1 (x1 int, x2 int, x3 text);
+CREATE TABLE temp_view_test.tt1 (y1 int, f2 int, f3 text);
+
+CREATE VIEW aliased_view_1 AS
+  select * from tt1
+    where exists (select 1 from tx1 where tt1.f1 = tx1.x1);
+CREATE VIEW aliased_view_2 AS
+  select * from tt1 a1
+    where exists (select 1 from tx1 where a1.f1 = tx1.x1);
+CREATE VIEW aliased_view_3 AS
+  select * from tt1
+    where exists (select 1 from tx1 a2 where tt1.f1 = a2.x1);
+CREATE VIEW aliased_view_4 AS
+  select * from temp_view_test.tt1
+    where exists (select 1 from tt1 where temp_view_test.tt1.y1 = tt1.f1);
+
+\d+ aliased_view_1
+\d+ aliased_view_2
+\d+ aliased_view_3
+\d+ aliased_view_4
+
+ALTER TABLE tx1 RENAME TO a1;
+
+\d+ aliased_view_1
+\d+ aliased_view_2
+\d+ aliased_view_3
+\d+ aliased_view_4
+
+ALTER TABLE tt1 RENAME TO a2;
+
+\d+ aliased_view_1
+\d+ aliased_view_2
+\d+ aliased_view_3
+\d+ aliased_view_4
+
+ALTER TABLE a1 RENAME TO tt1;
+
+\d+ aliased_view_1
+\d+ aliased_view_2
+\d+ aliased_view_3
+\d+ aliased_view_4
+
+ALTER TABLE a2 RENAME TO tx1;
+ALTER TABLE tx1 SET SCHEMA temp_view_test;
+
+\d+ aliased_view_1
+\d+ aliased_view_2
+\d+ aliased_view_3
+\d+ aliased_view_4
+
+ALTER TABLE temp_view_test.tt1 RENAME TO tmp1;
+ALTER TABLE temp_view_test.tmp1 SET SCHEMA testviewschm2;
+ALTER TABLE tmp1 RENAME TO tx1;
+
+\d+ aliased_view_1
+\d+ aliased_view_2
+\d+ aliased_view_3
+\d+ aliased_view_4
+
 DROP SCHEMA temp_view_test CASCADE;
 DROP SCHEMA testviewschm2 CASCADE;