mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-21 08:29:39 +08:00
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:
parent
7c45e3a3c6
commit
11e131854f
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user