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);
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);
}
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
/* 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) */
/* 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);
/* 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++)
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))
{
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
{
ExplainPropertyText(objecttag, objectname, es);
if (namespace != NULL)
ExplainPropertyText("Schema", namespace, es);
- ExplainPropertyText("Alias", rte->eref->aliasname, es);
+ if (refname != NULL)
+ ExplainPropertyText("Alias", refname, es);
}
}
* 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
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 */
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);
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);
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;
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;
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;
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;
/* 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);
* 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;
/* 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 */
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
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);
}
memset(&dpns, 0, sizeof(dpns));
dpns.rtable = query->rtable;
dpns.ctes = query->cteList;
+ set_rtable_names(&dpns, parentnamespace, NULL);
switch (query->commandType)
{
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)
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");
}
AttrNumber attnum;
int netlevelsup;
deparse_namespace *dpns;
- char *schemaname;
char *refname;
char *attname;
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)
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)
if (refname && (context->varprefix || attname == NULL))
{
- if (schemaname)
- appendStringInfo(buf, "%s.",
- quote_identifier(schemaname));
appendStringInfoString(buf, quote_identifier(refname));
appendStringInfoChar(buf, '.');
}
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);
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);
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.
{
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)
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;
}
/* 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;
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);
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)
-> 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)
-> 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)
-> 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)
-> 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)
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
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
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;
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)
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)
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;
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;
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)
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)
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
-> 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)
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)
-> 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)
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)
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
-> 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)
'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;