Commits

Andrew Dunstan committed 141a3f2

Backport 9.2devel RowExpr changed to 9.1.

  • Participants
  • Parent commits 421513b
  • Branches rowexpr91

Comments (0)

Files changed (14)

File contrib/hstore/expected/hstore.out

 
 -- records
 select hstore(v) from (values (1, 'foo', 1.2, 3::float8)) v(a,b,c,d);
-                     hstore                     
-------------------------------------------------
- "f1"=>"1", "f2"=>"foo", "f3"=>"1.2", "f4"=>"3"
+                   hstore                   
+--------------------------------------------
+ "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3"
 (1 row)
 
 create domain hstestdom1 as integer not null default 0;

File src/backend/executor/execQual.c

 				if (rowexpr->row_typeid == RECORDOID)
 				{
 					/* generic record, use runtime type assignment */
-					rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
+					rstate->tupdesc = ExecTypeFromExprList(rowexpr->args,
+														   rowexpr->colnames);
 					BlessTupleDesc(rstate->tupdesc);
 					/* we won't need to redo this at runtime */
 				}

File src/backend/executor/execTuples.c

 /*
  * ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
  *
- * Here we must make up an arbitrary set of field names.
+ * Caller must also supply a list of field names (String nodes).
  */
 TupleDesc
-ExecTypeFromExprList(List *exprList)
+ExecTypeFromExprList(List *exprList, List *namesList)
 {
 	TupleDesc	typeInfo;
-	ListCell   *l;
+	ListCell   *le;
+	ListCell   *ln;
 	int			cur_resno = 1;
-	char		fldname[NAMEDATALEN];
+
+	Assert(list_length(exprList) == list_length(namesList));
 
 	typeInfo = CreateTemplateTupleDesc(list_length(exprList), false);
 
-	foreach(l, exprList)
+	forboth(le, exprList, ln, namesList)
 	{
-		Node	   *e = lfirst(l);
-
-		sprintf(fldname, "f%d", cur_resno);
+		Node	   *e = lfirst(le);
+		char	   *n = strVal(lfirst(ln));
 
 		TupleDescInitEntry(typeInfo,
 						   cur_resno,
-						   fldname,
+						   n,
 						   exprType(e),
 						   exprTypmod(e),
 						   0);

File src/backend/executor/nodeValuesscan.c

 
 #include "executor/executor.h"
 #include "executor/nodeValuesscan.h"
+#include "parser/parsetree.h"
 #include "utils/memutils.h"
 
 
 ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 {
 	ValuesScanState *scanstate;
+	RangeTblEntry *rte = rt_fetch(node->scan.scanrelid,
+								  estate->es_range_table);
 	TupleDesc	tupdesc;
 	ListCell   *vtl;
 	int			i;
 	/*
 	 * get info about values list
 	 */
-	tupdesc = ExecTypeFromExprList((List *) linitial(node->values_lists));
+	tupdesc = ExecTypeFromExprList((List *) linitial(node->values_lists),
+								   rte->eref->colnames);
 
 	ExecAssignScanType(&scanstate->ss, tupdesc);
 

File src/backend/optimizer/path/allpaths.c

 		 * reconstitute the RestrictInfo layer.
 		 */
 		childquals = get_all_actual_clauses(rel->baserestrictinfo);
-		childquals = (List *) adjust_appendrel_attrs((Node *) childquals,
+		childquals = (List *) adjust_appendrel_attrs(root,
+													 (Node *) childquals,
 													 appinfo);
 		childqual = eval_const_expressions(root, (Node *)
 										   make_ands_explicit(childquals));
 		 * while constructing attr_widths estimates below, though.
 		 */
 		childrel->joininfo = (List *)
-			adjust_appendrel_attrs((Node *) rel->joininfo,
+			adjust_appendrel_attrs(root,
+								   (Node *) rel->joininfo,
 								   appinfo);
 		childrel->reltargetlist = (List *)
-			adjust_appendrel_attrs((Node *) rel->reltargetlist,
+			adjust_appendrel_attrs(root,
+								   (Node *) rel->reltargetlist,
 								   appinfo);
 
 		/*

File src/backend/optimizer/path/equivclass.c

 				Expr	   *child_expr;
 
 				child_expr = (Expr *)
-					adjust_appendrel_attrs((Node *) cur_em->em_expr,
+					adjust_appendrel_attrs(root,
+										   (Node *) cur_em->em_expr,
 										   appinfo);
 				(void) add_eq_member(cur_ec, child_expr, child_rel->relids,
 									 true, cur_em->em_datatype);

File src/backend/optimizer/plan/planner.c

 		 */
 		memcpy(&subroot, root, sizeof(PlannerInfo));
 		subroot.parse = (Query *)
-			adjust_appendrel_attrs((Node *) parse,
+			adjust_appendrel_attrs(root,
+								   (Node *) parse,
 								   appinfo);
 		subroot.hasInheritedTarget = true;
 		/* We needn't modify the child's append_rel_list */

File src/backend/optimizer/prep/prepunion.c

 #include "utils/selfuncs.h"
 
 
+typedef struct
+{
+	PlannerInfo *root;
+	AppendRelInfo *appinfo;
+} adjust_appendrel_attrs_context;
+
 static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,
 					   double tuple_fraction,
 					   List *colTypes, List *colCollations,
 static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
 					List *translated_vars);
 static Node *adjust_appendrel_attrs_mutator(Node *node,
-							   AppendRelInfo *context);
+							   adjust_appendrel_attrs_context *context);
 static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
 static List *adjust_inherited_tlist(List *tlist,
 					   AppendRelInfo *context);
  * maybe we should try to fold the two routines together.
  */
 Node *
-adjust_appendrel_attrs(Node *node, AppendRelInfo *appinfo)
+adjust_appendrel_attrs(PlannerInfo *root, Node *node, AppendRelInfo *appinfo)
 {
 	Node	   *result;
+	adjust_appendrel_attrs_context context;
+
+	context.root = root;
+	context.appinfo = appinfo;
 
 	/*
 	 * Must be prepared to start with a Query or a bare expression tree.
 
 		newnode = query_tree_mutator((Query *) node,
 									 adjust_appendrel_attrs_mutator,
-									 (void *) appinfo,
+									 (void *) &context,
 									 QTW_IGNORE_RC_SUBQUERIES);
 		if (newnode->resultRelation == appinfo->parent_relid)
 		{
 		result = (Node *) newnode;
 	}
 	else
-		result = adjust_appendrel_attrs_mutator(node, appinfo);
+		result = adjust_appendrel_attrs_mutator(node, &context);
 
 	return result;
 }
 
 static Node *
-adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
+adjust_appendrel_attrs_mutator(Node *node,
+							   adjust_appendrel_attrs_context *context)
 {
+	AppendRelInfo *appinfo = context->appinfo;
+
 	if (node == NULL)
 		return NULL;
 	if (IsA(node, Var))
 		Var		   *var = (Var *) copyObject(node);
 
 		if (var->varlevelsup == 0 &&
-			var->varno == context->parent_relid)
+			var->varno == appinfo->parent_relid)
 		{
-			var->varno = context->child_relid;
-			var->varnoold = context->child_relid;
+			var->varno = appinfo->child_relid;
+			var->varnoold = appinfo->child_relid;
 			if (var->varattno > 0)
 			{
 				Node	   *newnode;
 
-				if (var->varattno > list_length(context->translated_vars))
+				if (var->varattno > list_length(appinfo->translated_vars))
 					elog(ERROR, "attribute %d of relation \"%s\" does not exist",
-						 var->varattno, get_rel_name(context->parent_reloid));
-				newnode = copyObject(list_nth(context->translated_vars,
+						 var->varattno, get_rel_name(appinfo->parent_reloid));
+				newnode = copyObject(list_nth(appinfo->translated_vars,
 											  var->varattno - 1));
 				if (newnode == NULL)
 					elog(ERROR, "attribute %d of relation \"%s\" does not exist",
-						 var->varattno, get_rel_name(context->parent_reloid));
+						 var->varattno, get_rel_name(appinfo->parent_reloid));
 				return newnode;
 			}
 			else if (var->varattno == 0)
 				 * step to convert the tuple layout to the parent's rowtype.
 				 * Otherwise we have to generate a RowExpr.
 				 */
-				if (OidIsValid(context->child_reltype))
+				if (OidIsValid(appinfo->child_reltype))
 				{
-					Assert(var->vartype == context->parent_reltype);
-					if (context->parent_reltype != context->child_reltype)
+					Assert(var->vartype == appinfo->parent_reltype);
+					if (appinfo->parent_reltype != appinfo->child_reltype)
 					{
 						ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
 
 						r->arg = (Expr *) var;
-						r->resulttype = context->parent_reltype;
+						r->resulttype = appinfo->parent_reltype;
 						r->convertformat = COERCE_IMPLICIT_CAST;
 						r->location = -1;
 						/* Make sure the Var node has the right type ID, too */
-						var->vartype = context->child_reltype;
+						var->vartype = appinfo->child_reltype;
 						return (Node *) r;
 					}
 				}
 				{
 					/*
 					 * Build a RowExpr containing the translated variables.
+					 *
+					 * In practice var->vartype will always be RECORDOID here,
+					 * so we need to come up with some suitable column names.
+					 * We use the parent RTE's column names.
+					 *
+					 * Note: we can't get here for inheritance cases, so there
+					 * is no need to worry that translated_vars might contain
+					 * some dummy NULLs.
 					 */
 					RowExpr    *rowexpr;
 					List	   *fields;
+					RangeTblEntry *rte;
 
-					fields = (List *) copyObject(context->translated_vars);
+					rte = rt_fetch(appinfo->parent_relid,
+								   context->root->parse->rtable);
+					fields = (List *) copyObject(appinfo->translated_vars);
 					rowexpr = makeNode(RowExpr);
 					rowexpr->args = fields;
 					rowexpr->row_typeid = var->vartype;
 					rowexpr->row_format = COERCE_IMPLICIT_CAST;
-					rowexpr->colnames = NIL;
+					rowexpr->colnames = copyObject(rte->eref->colnames);
 					rowexpr->location = -1;
 
 					return (Node *) rowexpr;
 	{
 		CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
 
-		if (cexpr->cvarno == context->parent_relid)
-			cexpr->cvarno = context->child_relid;
+		if (cexpr->cvarno == appinfo->parent_relid)
+			cexpr->cvarno = appinfo->child_relid;
 		return (Node *) cexpr;
 	}
 	if (IsA(node, RangeTblRef))
 	{
 		RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
 
-		if (rtr->rtindex == context->parent_relid)
-			rtr->rtindex = context->child_relid;
+		if (rtr->rtindex == appinfo->parent_relid)
+			rtr->rtindex = appinfo->child_relid;
 		return (Node *) rtr;
 	}
 	if (IsA(node, JoinExpr))
 											  adjust_appendrel_attrs_mutator,
 												 (void *) context);
 		/* now fix JoinExpr's rtindex (probably never happens) */
-		if (j->rtindex == context->parent_relid)
-			j->rtindex = context->child_relid;
+		if (j->rtindex == appinfo->parent_relid)
+			j->rtindex = appinfo->child_relid;
 		return (Node *) j;
 	}
 	if (IsA(node, PlaceHolderVar))
 		/* now fix PlaceHolderVar's relid sets */
 		if (phv->phlevelsup == 0)
 			phv->phrels = adjust_relid_set(phv->phrels,
-										   context->parent_relid,
-										   context->child_relid);
+										   appinfo->parent_relid,
+										   appinfo->child_relid);
 		return (Node *) phv;
 	}
 	/* Shouldn't need to handle planner auxiliary nodes here */
 
 		/* adjust relid sets too */
 		newinfo->clause_relids = adjust_relid_set(oldinfo->clause_relids,
-												  context->parent_relid,
-												  context->child_relid);
+												  appinfo->parent_relid,
+												  appinfo->child_relid);
 		newinfo->required_relids = adjust_relid_set(oldinfo->required_relids,
-													context->parent_relid,
-													context->child_relid);
+													appinfo->parent_relid,
+													appinfo->child_relid);
 		newinfo->nullable_relids = adjust_relid_set(oldinfo->nullable_relids,
-													context->parent_relid,
-													context->child_relid);
+													appinfo->parent_relid,
+													appinfo->child_relid);
 		newinfo->left_relids = adjust_relid_set(oldinfo->left_relids,
-												context->parent_relid,
-												context->child_relid);
+												appinfo->parent_relid,
+												appinfo->child_relid);
 		newinfo->right_relids = adjust_relid_set(oldinfo->right_relids,
-												 context->parent_relid,
-												 context->child_relid);
+												 appinfo->parent_relid,
+												 appinfo->child_relid);
 
 		/*
 		 * Reset cached derivative fields, since these might need to have

File src/backend/optimizer/util/var.c

 			/* Must expand whole-row reference */
 			RowExpr    *rowexpr;
 			List	   *fields = NIL;
+			List	   *colnames = NIL;
 			AttrNumber	attnum;
-			ListCell   *l;
+			ListCell   *lv;
+			ListCell   *ln;
 
 			attnum = 0;
-			foreach(l, rte->joinaliasvars)
+			Assert(list_length(rte->joinaliasvars) == list_length(rte->eref->colnames));
+			forboth(lv, rte->joinaliasvars, ln, rte->eref->colnames)
 			{
-				newvar = (Node *) lfirst(l);
+				newvar = (Node *) lfirst(lv);
 				attnum++;
 				/* Ignore dropped columns */
 				if (IsA(newvar, Const))
 				/* (also takes care of setting inserted_sublink if needed) */
 				newvar = flatten_join_alias_vars_mutator(newvar, context);
 				fields = lappend(fields, newvar);
+				/* We need the names of non-dropped columns, too */
+				colnames = lappend(colnames, copyObject((Node *) lfirst(ln)));
 			}
 			rowexpr = makeNode(RowExpr);
 			rowexpr->args = fields;
 			rowexpr->row_typeid = var->vartype;
 			rowexpr->row_format = COERCE_IMPLICIT_CAST;
-			rowexpr->colnames = NIL;
+			rowexpr->colnames = colnames;
 			rowexpr->location = -1;
 
 			return (Node *) rowexpr;

File src/backend/parser/gram.y

 					RowExpr *r = makeNode(RowExpr);
 					r->args = $1;
 					r->row_typeid = InvalidOid;	/* not analyzed yet */
+					r->colnames = NIL;	/* to be filled in during analysis */
 					r->location = @1;
 					$$ = (Node *)r;
 				}

File src/backend/parser/parse_expr.c

 transformRowExpr(ParseState *pstate, RowExpr *r)
 {
 	RowExpr    *newr = makeNode(RowExpr);
+	char		fname[16];
+	int			fnum;
+	ListCell   *lc;
 
 	/* Transform the field expressions */
 	newr->args = transformExpressionList(pstate, r->args);
 	/* Barring later casting, we consider the type RECORD */
 	newr->row_typeid = RECORDOID;
 	newr->row_format = COERCE_IMPLICIT_CAST;
-	newr->colnames = NIL;		/* ROW() has anonymous columns */
+
+	/* ROW() has anonymous columns, so invent some field names */
+	newr->colnames = NIL;
+	fnum = 1;
+	foreach(lc, newr->args)
+	{
+		snprintf(fname, sizeof(fname), "f%d", fnum++);
+		newr->colnames = lappend(newr->colnames, makeString(pstrdup(fname)));
+	}
+
 	newr->location = r->location;
 
 	return (Node *) newr;

File src/include/executor/executor.h

 					  TupleDesc tupType);
 extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid);
 extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid);
-extern TupleDesc ExecTypeFromExprList(List *exprList);
+extern TupleDesc ExecTypeFromExprList(List *exprList, List *namesList);
 extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg);
 
 typedef struct TupOutputState

File src/include/nodes/primnodes.h

  * than vice versa.)  It is important not to assume that length(args) is
  * the same as the number of columns logically present in the rowtype.
  *
- * colnames is NIL in a RowExpr built from an ordinary ROW() expression.
- * It is provided in cases where we expand a whole-row Var into a RowExpr,
- * to retain the column alias names of the RTE that the Var referenced
- * (which would otherwise be very difficult to extract from the parsetree).
- * Like the args list, it is one-for-one with physical fields of the rowtype.
+ * colnames provides field names in cases where the names can't easily be
+ * obtained otherwise.  Names *must* be provided if row_typeid is RECORDOID.
+ * If row_typeid identifies a known composite type, colnames can be NIL to
+ * indicate the type's cataloged field names apply.  Note that colnames can
+ * be non-NIL even for a composite type, and typically is when the RowExpr
+ * was created by expanding a whole-row Var.  This is so that we can retain
+ * the column alias names of the RTE that the Var referenced (which would
+ * otherwise be very difficult to extract from the parsetree).  Like the
+ * args list, colnames is one-for-one with physical fields of the rowtype.
  */
 typedef struct RowExpr
 {

File src/include/optimizer/prep.h

 
 extern void expand_inherited_tables(PlannerInfo *root);
 
-extern Node *adjust_appendrel_attrs(Node *node, AppendRelInfo *appinfo);
+extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
+									AppendRelInfo *appinfo);
 
 #endif   /* PREP_H */