Improve ruleutils.c's heuristics for dealing with rangetable aliases.

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.
This commit is contained in:
Tom Lane 2012-09-21 19:03:10 -04:00
parent 7c45e3a3c6
commit 11e131854f
12 changed files with 886 additions and 226 deletions

View 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);
}
}

View 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)
if (rte->joinaliasvars == NIL)
elog(ERROR, "cannot decompile join alias var in plan tree");
if (attnum > 0)
{
/*
* 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 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;
aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
if (IsA(aliasvar, Var))
{
Var *aliasvar;
aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
if (IsA(aliasvar, Var))
{
return get_variable(aliasvar, var->varlevelsup + levelsup,
istoplevel, context);
}
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;
}

View 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;

View 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);

View 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)

View 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)

View 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;

View 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

View 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)

View 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)

View 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)

View 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;