fix-ruleutils-alias-selection-2.patch

text/x-patch

Filename: fix-ruleutils-alias-selection-2.patch
Type: text/x-patch
Part: 0
Message: Re: Confusing EXPLAIN output in case of inherited tables

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/backend/commands/explain.c 0 0
src/backend/utils/adt/ruleutils.c 0 0
src/include/commands/explain.h 0 0
src/include/utils/builtins.h 0 0
src/test/regress/expected/aggregates.out 0 0
src/test/regress/expected/alter_table.out 0 0
src/test/regress/expected/inherit.out 0 0
src/test/regress/expected/select_views_1.out 0 0
src/test/regress/expected/select_views.out 0 0
src/test/regress/expected/with.out 0 0
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 1e8f618a3476b9e171658900f92532b87b3cde97..33252a8e205abc3726ffb32c5615ef6fa64e28a8 100644
*** a/src/backend/commands/explain.c
--- b/src/backend/commands/explain.c
*************** static void ExplainOneQuery(Query *query
*** 51,56 ****
--- 51,60 ----
  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);
*************** ExplainOnePlan(PlannedStmt *plannedstmt,
*** 539,547 ****
--- 543,555 ----
  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);
  }
  
*************** elapsed_time(instr_time *starttime)
*** 641,646 ****
--- 649,780 ----
  }
  
  /*
+  * 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
   *
*************** show_plan_tlist(PlanState *planstate, Li
*** 1440,1446 ****
  	/* Set up deparsing context */
  	context = deparse_context_for_planstate((Node *) planstate,
  											ancestors,
! 											es->rtable);
  	useprefix = list_length(es->rtable) > 1;
  
  	/* Deparse each result column (we now include resjunk ones) */
--- 1574,1581 ----
  	/* Set up deparsing context */
  	context = deparse_context_for_planstate((Node *) planstate,
  											ancestors,
! 											es->rtable,
! 											es->rtable_names);
  	useprefix = list_length(es->rtable) > 1;
  
  	/* Deparse each result column (we now include resjunk ones) */
*************** show_expression(Node *node, const char *
*** 1471,1477 ****
  	/* Set up deparsing context */
  	context = deparse_context_for_planstate((Node *) planstate,
  											ancestors,
! 											es->rtable);
  
  	/* Deparse the expression */
  	exprstr = deparse_expression(node, context, useprefix, false);
--- 1606,1613 ----
  	/* Set up deparsing context */
  	context = deparse_context_for_planstate((Node *) planstate,
  											ancestors,
! 											es->rtable,
! 											es->rtable_names);
  
  	/* Deparse the expression */
  	exprstr = deparse_expression(node, context, useprefix, false);
*************** show_sort_keys_common(PlanState *plansta
*** 1573,1579 ****
  	/* Set up deparsing context */
  	context = deparse_context_for_planstate((Node *) planstate,
  											ancestors,
! 											es->rtable);
  	useprefix = (list_length(es->rtable) > 1 || es->verbose);
  
  	for (keyno = 0; keyno < nkeys; keyno++)
--- 1709,1716 ----
  	/* Set up deparsing context */
  	context = deparse_context_for_planstate((Node *) planstate,
  											ancestors,
! 											es->rtable,
! 											es->rtable_names);
  	useprefix = (list_length(es->rtable) > 1 || es->verbose);
  
  	for (keyno = 0; keyno < nkeys; keyno++)
*************** ExplainTargetRel(Plan *plan, Index rti, 
*** 1813,1820 ****
--- 1950,1959 ----
  	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))
  	{
*************** ExplainTargetRel(Plan *plan, Index rti, 
*** 1887,1896 ****
  							 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));
  	}
  	else
  	{
--- 2026,2034 ----
  							 quote_identifier(objectname));
  		else if (objectname != NULL)
  			appendStringInfo(es->str, " %s", quote_identifier(objectname));
! 		if (refname != NULL &&
! 			(objectname == NULL || strcmp(refname, objectname) != 0))
! 			appendStringInfo(es->str, " %s", quote_identifier(refname));
  	}
  	else
  	{
*************** ExplainTargetRel(Plan *plan, Index rti, 
*** 1898,1904 ****
  			ExplainPropertyText(objecttag, objectname, es);
  		if (namespace != NULL)
  			ExplainPropertyText("Schema", namespace, es);
! 		ExplainPropertyText("Alias", rte->eref->aliasname, es);
  	}
  }
  
--- 2036,2043 ----
  			ExplainPropertyText(objecttag, objectname, es);
  		if (namespace != NULL)
  			ExplainPropertyText("Schema", namespace, es);
! 		if (refname != NULL)
! 			ExplainPropertyText("Alias", refname, es);
  	}
  }
  
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 08396431384a8f8581492304c8c916c41e4b9516..c8d7d9c21b3a4398008160da844e83e8330e8ad4 100644
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
*************** typedef struct
*** 104,110 ****
   * 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.
   *
   * When deparsing plan trees, there is always just a single item in the
   * deparse_namespace list (since a plan tree never contains Vars with
--- 104,112 ----
   * 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.  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
*** 119,124 ****
--- 121,127 ----
  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 text *pg_get_expr_worker(text *ex
*** 172,177 ****
--- 175,185 ----
  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);
*************** static void get_rule_windowspec(WindowCl
*** 212,219 ****
  					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);
--- 220,225 ----
*************** pg_get_triggerdef_worker(Oid trigid, boo
*** 676,682 ****
  		oldrte->rtekind = RTE_RELATION;
  		oldrte->relid = trigrec->tgrelid;
  		oldrte->relkind = relkind;
! 		oldrte->eref = makeAlias("old", NIL);
  		oldrte->lateral = false;
  		oldrte->inh = false;
  		oldrte->inFromCl = true;
--- 682,689 ----
  		oldrte->rtekind = RTE_RELATION;
  		oldrte->relid = trigrec->tgrelid;
  		oldrte->relkind = relkind;
! 		oldrte->alias = makeAlias("old", NIL);
! 		oldrte->eref = oldrte->alias;
  		oldrte->lateral = false;
  		oldrte->inh = false;
  		oldrte->inFromCl = true;
*************** pg_get_triggerdef_worker(Oid trigid, boo
*** 685,691 ****
  		newrte->rtekind = RTE_RELATION;
  		newrte->relid = trigrec->tgrelid;
  		newrte->relkind = relkind;
! 		newrte->eref = makeAlias("new", NIL);
  		newrte->lateral = false;
  		newrte->inh = false;
  		newrte->inFromCl = true;
--- 692,699 ----
  		newrte->rtekind = RTE_RELATION;
  		newrte->relid = trigrec->tgrelid;
  		newrte->relkind = relkind;
! 		newrte->alias = makeAlias("new", NIL);
! 		newrte->eref = newrte->alias;
  		newrte->lateral = false;
  		newrte->inh = false;
  		newrte->inFromCl = true;
*************** pg_get_triggerdef_worker(Oid trigid, boo
*** 694,699 ****
--- 702,708 ----
  		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;
*************** deparse_context_for(const char *aliasnam
*** 2176,2182 ****
  	rte->rtekind = RTE_RELATION;
  	rte->relid = relid;
  	rte->relkind = RELKIND_RELATION;	/* no need for exactness here */
! 	rte->eref = makeAlias(aliasname, NIL);
  	rte->lateral = false;
  	rte->inh = false;
  	rte->inFromCl = true;
--- 2185,2192 ----
  	rte->rtekind = RTE_RELATION;
  	rte->relid = relid;
  	rte->relkind = RELKIND_RELATION;	/* no need for exactness here */
! 	rte->alias = makeAlias(aliasname, NIL);
! 	rte->eref = rte->alias;
  	rte->lateral = false;
  	rte->inh = false;
  	rte->inFromCl = true;
*************** deparse_context_for(const char *aliasnam
*** 2184,2189 ****
--- 2194,2200 ----
  	/* 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);
*************** deparse_context_for(const char *aliasnam
*** 2209,2221 ****
   * 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.
   */
  List *
  deparse_context_for_planstate(Node *planstate, List *ancestors,
! 							  List *rtable)
  {
  	deparse_namespace *dpns;
  
--- 2220,2233 ----
   * 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, 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_names)
  {
  	deparse_namespace *dpns;
  
*************** deparse_context_for_planstate(Node *plan
*** 2223,2228 ****
--- 2235,2241 ----
  
  	/* 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 */
*************** deparse_context_for_planstate(Node *plan
*** 2234,2239 ****
--- 2247,2388 ----
  }
  
  /*
+  * 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
   *
*************** make_ruledef(StringInfo buf, HeapTuple r
*** 2534,2539 ****
--- 2683,2689 ----
  		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);
  	}
*************** get_query_def(Query *query, StringInfo b
*** 2680,2685 ****
--- 2830,2836 ----
  	memset(&dpns, 0, sizeof(dpns));
  	dpns.rtable = query->rtable;
  	dpns.ctes = query->cteList;
+ 	set_rtable_names(&dpns, parentnamespace, NULL);
  
  	switch (query->commandType)
  	{
*************** get_select_query_def(Query *query, depar
*** 2899,2905 ****
  		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)
--- 3050,3055 ----
*************** get_select_query_def(Query *query, depar
*** 2912,2918 ****
  				appendContextKeyword(context, " FOR SHARE",
  									 -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
  			appendStringInfo(buf, " OF %s",
! 							 quote_identifier(rte->eref->aliasname));
  			if (rc->noWait)
  				appendStringInfo(buf, " NOWAIT");
  		}
--- 3062,3069 ----
  				appendContextKeyword(context, " FOR SHARE",
  									 -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
  			appendStringInfo(buf, " OF %s",
! 							 quote_identifier(get_rtable_name(rc->rti,
! 															  context)));
  			if (rc->noWait)
  				appendStringInfo(buf, " NOWAIT");
  		}
*************** get_variable(Var *var, int levelsup, boo
*** 3854,3860 ****
  	AttrNumber	attnum;
  	int			netlevelsup;
  	deparse_namespace *dpns;
- 	char	   *schemaname;
  	char	   *refname;
  	char	   *attname;
  
--- 4005,4010 ----
*************** get_variable(Var *var, int levelsup, boo
*** 3874,3879 ****
--- 4024,4030 ----
  	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)
*************** get_variable(Var *var, int levelsup, boo
*** 3993,4053 ****
  		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 (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 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))
! 				{
! 					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;
  		}
  	}
  
  	if (attnum == InvalidAttrNumber)
--- 4144,4184 ----
  		return 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->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))
! 			{
! 				return get_variable(aliasvar, var->varlevelsup + levelsup,
! 									istoplevel, context);
  			}
  		}
+ 
+ 		/*
+ 		 * 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)
*************** get_variable(Var *var, int levelsup, boo
*** 4057,4065 ****
  
  	if (refname && (context->varprefix || attname == NULL))
  	{
- 		if (schemaname)
- 			appendStringInfo(buf, "%s.",
- 							 quote_identifier(schemaname));
  		appendStringInfoString(buf, quote_identifier(refname));
  		appendStringInfoChar(buf, '.');
  	}
--- 4188,4193 ----
*************** get_name_for_var_field(Var *var, int fie
*** 4289,4294 ****
--- 4417,4423 ----
  						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);
*************** get_name_for_var_field(Var *var, int fie
*** 4406,4411 ****
--- 4535,4541 ----
  						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);
*************** get_name_for_var_field(Var *var, int fie
*** 4467,4513 ****
  	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.
--- 4597,4602 ----
*************** get_from_clause_item(Node *jtnode, Query
*** 6649,6654 ****
--- 6738,6744 ----
  	{
  		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)
*************** get_from_clause_item(Node *jtnode, Query
*** 6688,6719 ****
  
  		if (rte->alias != NULL)
  		{
! 			appendStringInfo(buf, " %s",
! 							 quote_identifier(rte->alias->aliasname));
  			gavealias = true;
  		}
! 		else if (rte->rtekind == RTE_RELATION &&
! 			strcmp(rte->eref->aliasname, get_relation_name(rte->relid)) != 0)
  		{
  			/*
! 			 * 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.
  			 */
! 			appendStringInfo(buf, " %s",
! 							 quote_identifier(rte->eref->aliasname));
! 			gavealias = true;
  		}
  		else if (rte->rtekind == RTE_FUNCTION)
  		{
  			/*
! 			 * For a function RTE, always give an 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));
  			gavealias = true;
  		}
  
--- 6778,6808 ----
  
  		if (rte->alias != NULL)
  		{
! 			/* Always print alias if user provided one */
! 			appendStringInfo(buf, " %s", quote_identifier(refname));
  			gavealias = true;
  		}
! 		else if (rte->rtekind == RTE_RELATION)
  		{
  			/*
! 			 * 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).
  			 */
! 			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 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(refname));
  			gavealias = true;
  		}
  
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index c4215da1e329102df3289f9c88a732f51f90503e..4227f4e59c3d07cee8d0fc95660010705ce5e13b 100644
*** a/src/include/commands/explain.h
--- b/src/include/commands/explain.h
*************** typedef struct ExplainState
*** 37,42 ****
--- 37,43 ----
  	/* 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;
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index c9c665dae09c08a726c04ed58591d63e3b1b3347..5bc3a75856d8efe53ed50064ae71d15711b2beaf 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern char *deparse_expression(Node *ex
*** 654,660 ****
  				   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);
  extern const char *quote_identifier(const char *ident);
  extern char *quote_qualified_identifier(const char *qualifier,
  						   const char *ident);
--- 654,662 ----
  				   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_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);
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 6ca73a0ed73fc9c2fa18701f17fa34b7f12db131..7286f1aa446ffc0f3b0d0527daf2a5bbba34713e 100644
*** a/src/test/regress/expected/aggregates.out
--- b/src/test/regress/expected/aggregates.out
*************** insert into minmaxtest2 values(15), (16)
*** 705,736 ****
  insert into minmaxtest3 values(17), (18);
  explain (costs off)
    select min(f1), max(f1) from minmaxtest;
!                                         QUERY PLAN                                         
! -------------------------------------------------------------------------------------------
   Result
     InitPlan 1 (returns $0)
       ->  Limit
             ->  Merge Append
!                  Sort Key: public.minmaxtest.f1
                   ->  Index Only Scan using minmaxtesti on minmaxtest
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan using minmaxtest1i on minmaxtest1 minmaxtest
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan Backward using minmaxtest2i on minmaxtest2 minmaxtest
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan using minmaxtest3i on minmaxtest3 minmaxtest
                         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
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan using minmaxtest2i on minmaxtest2 minmaxtest
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan Backward using minmaxtest3i on minmaxtest3 minmaxtest
                         Index Cond: (f1 IS NOT NULL)
  (25 rows)
  
--- 705,736 ----
  insert into minmaxtest3 values(17), (18);
  explain (costs off)
    select min(f1), max(f1) from minmaxtest;
!                                           QUERY PLAN                                          
! ----------------------------------------------------------------------------------------------
   Result
     InitPlan 1 (returns $0)
       ->  Limit
             ->  Merge Append
!                  Sort Key: minmaxtest.f1
                   ->  Index Only Scan using minmaxtesti on minmaxtest
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan using minmaxtest1i on minmaxtest1
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan Backward using minmaxtest2i on minmaxtest2
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan using minmaxtest3i on minmaxtest3
                         Index Cond: (f1 IS NOT NULL)
     InitPlan 2 (returns $1)
       ->  Limit
             ->  Merge Append
!                  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 minmaxtest1_1
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan using minmaxtest2i on minmaxtest2 minmaxtest2_1
                         Index Cond: (f1 IS NOT NULL)
!                  ->  Index Only Scan Backward using minmaxtest3i on minmaxtest3 minmaxtest3_1
                         Index Cond: (f1 IS NOT NULL)
  (25 rows)
  
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 453a3894b202873b31dada5727e7e09ae3f2f142..c22d74c7b562e86d6e839b4ab3a87d3cf7a29149 100644
*** a/src/test/regress/expected/alter_table.out
--- b/src/test/regress/expected/alter_table.out
*************** explain (costs off) select * from nv_par
*** 391,399 ****
     ->  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
                 Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
!          ->  Seq Scan on nv_child_2011 nv_parent
                 Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
  (8 rows)
  
--- 391,399 ----
     ->  Append
           ->  Seq Scan on nv_parent
                 Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
!          ->  Seq Scan on nv_child_2010
                 Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
!          ->  Seq Scan on nv_child_2011
                 Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
  (8 rows)
  
*************** explain (costs off) select * from nv_par
*** 405,413 ****
     ->  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
                 Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
!          ->  Seq Scan on nv_child_2011 nv_parent
                 Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
  (8 rows)
  
--- 405,413 ----
     ->  Append
           ->  Seq Scan on nv_parent
                 Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
!          ->  Seq Scan on nv_child_2010
                 Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
!          ->  Seq Scan on nv_child_2011
                 Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
  (8 rows)
  
*************** explain (costs off) select * from nv_par
*** 418,428 ****
     ->  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
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
!          ->  Seq Scan on nv_child_2011 nv_parent
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
!          ->  Seq Scan on nv_child_2009 nv_parent
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
  (10 rows)
  
--- 418,428 ----
     ->  Append
           ->  Seq Scan on nv_parent
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
!          ->  Seq Scan on nv_child_2010
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
!          ->  Seq Scan on nv_child_2011
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
!          ->  Seq Scan on nv_child_2009
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
  (10 rows)
  
*************** explain (costs off) select * from nv_par
*** 435,443 ****
     ->  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
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
!          ->  Seq Scan on nv_child_2009 nv_parent
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
  (8 rows)
  
--- 435,443 ----
     ->  Append
           ->  Seq Scan on nv_parent
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
!          ->  Seq Scan on nv_child_2010
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
!          ->  Seq Scan on nv_child_2009
                 Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
  (8 rows)
  
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 25adcd2346118853ef57943bd22d9b80667ca560..906a928b0c0a44ff1dcac5b3b9803cd508ec7533 100644
*** a/src/test/regress/expected/inherit.out
--- b/src/test/regress/expected/inherit.out
*************** analyze patest1;
*** 1105,1121 ****
  analyze patest2;
  explain (costs off)
  select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1;
!                         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 Cond: (id = int4_tbl.f1)
!          ->  Index Scan using patest2i on patest2 patest0
                 Index Cond: (id = int4_tbl.f1)
  (10 rows)
  
--- 1105,1121 ----
  analyze patest2;
  explain (costs off)
  select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1;
!                     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
                 Index Cond: (id = int4_tbl.f1)
!          ->  Index Scan using patest2i on patest2
                 Index Cond: (id = int4_tbl.f1)
  (10 rows)
  
*************** select * from patest0 join (select f1 fr
*** 1130,1146 ****
  drop index patest2i;
  explain (costs off)
  select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1;
!                         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 Cond: (id = int4_tbl.f1)
!          ->  Seq Scan on patest2 patest0
                 Filter: (int4_tbl.f1 = id)
  (10 rows)
  
--- 1130,1146 ----
  drop index patest2i;
  explain (costs off)
  select * from patest0 join (select f1 from int4_tbl limit 1) ss on id = f1;
!                     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
                 Index Cond: (id = int4_tbl.f1)
!          ->  Seq Scan on patest2
                 Filter: (int4_tbl.f1 = id)
  (10 rows)
  
*************** insert into matest3 (name) values ('Test
*** 1178,1199 ****
  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                                    
! ---------------------------------------------------------------------------------
   Sort
!    Output: public.matest0.id, public.matest0.name, ((1 - public.matest0.id))
!    Sort Key: ((1 - public.matest0.id))
     ->  Result
!          Output: public.matest0.id, public.matest0.name, (1 - public.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
  (14 rows)
  
  select * from matest0 order by 1-id;
--- 1178,1199 ----
  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                         
! ------------------------------------------------------------
   Sort
!    Output: matest0.id, matest0.name, ((1 - matest0.id))
!    Sort Key: ((1 - matest0.id))
     ->  Result
!          Output: matest0.id, matest0.name, (1 - matest0.id)
           ->  Append
                 ->  Seq Scan on public.matest0
!                      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;
*************** select * from matest0 order by 1-id;
*** 1210,1232 ****
  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                                          
! ---------------------------------------------------------------------------------------------
   Result
!    Output: public.matest0.id, public.matest0.name, ((1 - public.matest0.id))
     ->  Merge Append
!          Sort Key: ((1 - public.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)
           ->  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)
  (15 rows)
  
  select * from matest0 order by 1-id;
--- 1210,1232 ----
  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                               
! ------------------------------------------------------------------------
   Result
!    Output: matest0.id, matest0.name, ((1 - matest0.id))
     ->  Merge Append
!          Sort Key: ((1 - matest0.id))
           ->  Index Scan using matest0i on public.matest0
!                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: 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;
*************** SELECT thousand, tenthous FROM tenk1
*** 1258,1272 ****
  UNION ALL
  SELECT thousand, thousand FROM tenk1
  ORDER BY thousand, tenthous;
!                               QUERY PLAN                               
! -----------------------------------------------------------------------
   Result
     ->  Merge Append
!          Sort Key: public.tenk1.thousand, public.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
  (7 rows)
  
  explain (costs off)
--- 1258,1272 ----
  UNION ALL
  SELECT thousand, thousand FROM tenk1
  ORDER BY thousand, tenthous;
!                                   QUERY PLAN                                   
! -------------------------------------------------------------------------------
   Result
     ->  Merge Append
!          Sort Key: tenk1.thousand, tenk1.tenthous
           ->  Index Only Scan using tenk1_thous_tenthous on tenk1
           ->  Sort
!                Sort Key: tenk1_1.thousand, tenk1_1.thousand
!                ->  Index Only Scan using tenk1_thous_tenthous on tenk1 tenk1_1
  (7 rows)
  
  explain (costs off)
*************** SELECT thousand, tenthous, thousand+tent
*** 1274,1288 ****
  UNION ALL
  SELECT 42, 42, hundred FROM tenk1
  ORDER BY thousand, tenthous;
!                            QUERY PLAN                            
! -----------------------------------------------------------------
   Result
     ->  Merge Append
!          Sort Key: public.tenk1.thousand, public.tenk1.tenthous
           ->  Index Only Scan using tenk1_thous_tenthous on tenk1
           ->  Sort
                 Sort Key: (42), (42)
!                ->  Index Only Scan using tenk1_hundred on tenk1
  (7 rows)
  
  explain (costs off)
--- 1274,1288 ----
  UNION ALL
  SELECT 42, 42, hundred FROM tenk1
  ORDER BY thousand, tenthous;
!                                QUERY PLAN                               
! ------------------------------------------------------------------------
   Result
     ->  Merge Append
!          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 tenk1_1
  (7 rows)
  
  explain (costs off)
*************** SELECT thousand, tenthous FROM tenk1
*** 1290,1304 ****
  UNION ALL
  SELECT thousand, random()::integer FROM tenk1
  ORDER BY thousand, tenthous;
!                               QUERY PLAN                               
! -----------------------------------------------------------------------
   Result
     ->  Merge Append
!          Sort Key: public.tenk1.thousand, public.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
  (7 rows)
  
  -- Check min/max aggregate optimization
--- 1290,1304 ----
  UNION ALL
  SELECT thousand, random()::integer FROM tenk1
  ORDER BY thousand, tenthous;
!                                   QUERY PLAN                                   
! -------------------------------------------------------------------------------
   Result
     ->  Merge Append
!          Sort Key: tenk1.thousand, tenk1.tenthous
           ->  Index Only Scan using tenk1_thous_tenthous on tenk1
           ->  Sort
!                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
diff --git a/src/test/regress/expected/select_views.out b/src/test/regress/expected/select_views.out
index e4eba1ae36c8715d2f414db413feb68e71c1c575..24bbff9d2e4727529d470454f012db0f9a774776 100644
*** a/src/test/regress/expected/select_views.out
--- b/src/test/regress/expected/select_views.out
*************** EXPLAIN (COSTS OFF) SELECT * FROM my_cre
*** 1421,1430 ****
           ->  Subquery Scan on l
                 Filter: f_leak(l.cnum)
                 ->  Hash Join
!                      Hash Cond: (r.cid = l.cid)
!                      ->  Seq Scan on credit_card r
                       ->  Hash
!                            ->  Seq Scan on customer l
                                   Filter: (name = ("current_user"())::text)
  (13 rows)
  
--- 1421,1430 ----
           ->  Subquery Scan on l
                 Filter: f_leak(l.cnum)
                 ->  Hash Join
!                      Hash Cond: (r_1.cid = l_1.cid)
!                      ->  Seq Scan on credit_card r_1
                       ->  Hash
!                            ->  Seq Scan on customer l_1
                                   Filter: (name = ("current_user"())::text)
  (13 rows)
  
*************** EXPLAIN (COSTS OFF) SELECT * FROM my_cre
*** 1452,1459 ****
                 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
                             ->  Seq Scan on customer l
                                   Filter: (name = ("current_user"())::text)
--- 1452,1459 ----
                 Filter: ((ymd >= '10-01-2011'::date) AND (ymd < '11-01-2011'::date))
           ->  Materialize
                 ->  Hash Join
!                      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)
diff --git a/src/test/regress/expected/select_views_1.out b/src/test/regress/expected/select_views_1.out
index 94b439825c49b25651a7722f301d203f9d6c958f..ec6e938cb1d3a30d56bc8b0ceeafd0a6f8d6fe43 100644
*** a/src/test/regress/expected/select_views_1.out
--- b/src/test/regress/expected/select_views_1.out
*************** EXPLAIN (COSTS OFF) SELECT * FROM my_cre
*** 1421,1430 ****
           ->  Subquery Scan on l
                 Filter: f_leak(l.cnum)
                 ->  Hash Join
!                      Hash Cond: (r.cid = l.cid)
!                      ->  Seq Scan on credit_card r
                       ->  Hash
!                            ->  Seq Scan on customer l
                                   Filter: (name = ("current_user"())::text)
  (13 rows)
  
--- 1421,1430 ----
           ->  Subquery Scan on l
                 Filter: f_leak(l.cnum)
                 ->  Hash Join
!                      Hash Cond: (r_1.cid = l_1.cid)
!                      ->  Seq Scan on credit_card r_1
                       ->  Hash
!                            ->  Seq Scan on customer l_1
                                   Filter: (name = ("current_user"())::text)
  (13 rows)
  
*************** EXPLAIN (COSTS OFF) SELECT * FROM my_cre
*** 1452,1459 ****
                 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
                             ->  Seq Scan on customer l
                                   Filter: (name = ("current_user"())::text)
--- 1452,1459 ----
                 Filter: ((ymd >= '10-01-2011'::date) AND (ymd < '11-01-2011'::date))
           ->  Materialize
                 ->  Hash Join
!                      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)
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 38cfb8c7276c9310a60375f0f70d56b8c14eb970..671f293b68de95617b6f953573a31fda3fde176f 100644
*** a/src/test/regress/expected/with.out
--- b/src/test/regress/expected/with.out
*************** SELECT * FROM parent;
*** 2006,2013 ****
  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                    
! --------------------------------------------------
   Delete on public.a
     CTE wcte
       ->  Insert on public.int8_tbl
--- 2006,2013 ----
  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                   
! ------------------------------------------------
   Delete on public.a
     CTE wcte
       ->  Insert on public.int8_tbl
*************** DELETE FROM a USING wcte WHERE aa = q2;
*** 2015,2045 ****
             ->  Result
                   Output: 42::bigint, 47::bigint
     ->  Nested Loop
!          Output: public.a.ctid, wcte.*
!          Join Filter: (public.a.aa = wcte.q2)
           ->  Seq Scan on public.a
!                Output: public.a.ctid, public.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
           ->  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
           ->  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
           ->  CTE Scan on wcte
                 Output: wcte.*, wcte.q2
  (34 rows)
--- 2015,2045 ----
             ->  Result
                   Output: 42::bigint, 47::bigint
     ->  Nested Loop
!          Output: a.ctid, wcte.*
!          Join Filter: (a.aa = wcte.q2)
           ->  Seq Scan on public.a
!                Output: a.ctid, a.aa
           ->  CTE Scan on wcte
                 Output: wcte.*, wcte.q2
     ->  Nested Loop
!          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: 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: 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)