Skip to content
Snippets Groups Projects
Select Git revision
  • benchmark-tools
  • postgres-lambda
  • master default
  • REL9_4_25
  • REL9_5_20
  • REL9_6_16
  • REL_10_11
  • REL_11_6
  • REL_12_1
  • REL_12_0
  • REL_12_RC1
  • REL_12_BETA4
  • REL9_4_24
  • REL9_5_19
  • REL9_6_15
  • REL_10_10
  • REL_11_5
  • REL_12_BETA3
  • REL9_4_23
  • REL9_5_18
  • REL9_6_14
  • REL_10_9
  • REL_11_4
23 results

parse_target.c

Blame
  • parse_target.c 47.25 KiB
    /*-------------------------------------------------------------------------
     *
     * parse_target.c
     *	  handle target lists
     *
     * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
     * Portions Copyright (c) 1994, Regents of the University of California
     *
     *
     * IDENTIFICATION
     *	  src/backend/parser/parse_target.c
     *
     *-------------------------------------------------------------------------
     */
    #include "postgres.h"
    
    #include "catalog/pg_type.h"
    #include "commands/dbcommands.h"
    #include "funcapi.h"
    #include "miscadmin.h"
    #include "nodes/makefuncs.h"
    #include "nodes/nodeFuncs.h"
    #include "parser/parsetree.h"
    #include "parser/parse_coerce.h"
    #include "parser/parse_expr.h"
    #include "parser/parse_func.h"
    #include "parser/parse_relation.h"
    #include "parser/parse_target.h"
    #include "parser/parse_type.h"
    #include "utils/builtins.h"
    #include "utils/lsyscache.h"
    #include "utils/rel.h"
    #include "utils/typcache.h"
    
    
    static void markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
    					 Var *var, int levelsup);
    static Node *transformAssignmentIndirection(ParseState *pstate,
    							   Node *basenode,
    							   const char *targetName,
    							   bool targetIsArray,
    							   Oid targetTypeId,
    							   int32 targetTypMod,
    							   Oid targetCollation,
    							   ListCell *indirection,
    							   Node *rhs,
    							   int location);
    static Node *transformAssignmentSubscripts(ParseState *pstate,
    							  Node *basenode,
    							  const char *targetName,
    							  Oid targetTypeId,
    							  int32 targetTypMod,
    							  Oid targetCollation,
    							  List *subscripts,
    							  bool isSlice,
    							  ListCell *next_indirection,
    							  Node *rhs,
    							  int location);
    static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
    					bool targetlist);
    static List *ExpandAllTables(ParseState *pstate, int location);
    static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
    					  bool targetlist);
    static List *ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
    				  int location, bool targetlist);
    static List *ExpandRowReference(ParseState *pstate, Node *expr,
    				   bool targetlist);
    static int	FigureColnameInternal(Node *node, char **name);
    
    
    /*
     * transformTargetEntry()
     *	Transform any ordinary "expression-type" node into a targetlist entry.
     *	This is exported so that parse_clause.c can generate targetlist entries
     *	for ORDER/GROUP BY items that are not already in the targetlist.
     *
     * node		the (untransformed) parse tree for the value expression.
     * expr		the transformed expression, or NULL if caller didn't do it yet.
     * colname	the column name to be assigned, or NULL if none yet set.
     * resjunk	true if the target should be marked resjunk, ie, it is not
     *			wanted in the final projected tuple.
     */
    TargetEntry *
    transformTargetEntry(ParseState *pstate,
    					 Node *node,
    					 Node *expr,
    					 char *colname,
    					 bool resjunk)
    {
    	/* Transform the node if caller didn't do it already */
    	if (expr == NULL)
    		expr = transformExpr(pstate, node);
    
    	if (colname == NULL && !resjunk)
    	{
    		/*
    		 * Generate a suitable column name for a column without any explicit
    		 * 'AS ColumnName' clause.
    		 */
    		colname = FigureColname(node);
    	}
    
    	return makeTargetEntry((Expr *) expr,
    						   (AttrNumber) pstate->p_next_resno++,
    						   colname,
    						   resjunk);
    }
    
    
    /*
     * transformTargetList()
     * Turns a list of ResTarget's into a list of TargetEntry's.
     *
     * At this point, we don't care whether we are doing SELECT, INSERT,
     * or UPDATE; we just transform the given expressions (the "val" fields).
     */
    List *
    transformTargetList(ParseState *pstate, List *targetlist)
    {
    	List	   *p_target = NIL;
    	ListCell   *o_target;
    
    	foreach(o_target, targetlist)
    	{
    		ResTarget  *res = (ResTarget *) lfirst(o_target);
    
    		/*
    		 * Check for "something.*".  Depending on the complexity of the
    		 * "something", the star could appear as the last field in ColumnRef,
    		 * or as the last indirection item in A_Indirection.
    		 */
    		if (IsA(res->val, ColumnRef))
    		{
    			ColumnRef  *cref = (ColumnRef *) res->val;
    
    			if (IsA(llast(cref->fields), A_Star))
    			{
    				/* It is something.*, expand into multiple items */
    				p_target = list_concat(p_target,
    									   ExpandColumnRefStar(pstate, cref,
    														   true));
    				continue;
    			}
    		}
    		else if (IsA(res->val, A_Indirection))
    		{
    			A_Indirection *ind = (A_Indirection *) res->val;
    
    			if (IsA(llast(ind->indirection), A_Star))
    			{
    				/* It is something.*, expand into multiple items */
    				p_target = list_concat(p_target,
    									   ExpandIndirectionStar(pstate, ind,
    															 true));
    				continue;
    			}
    		}
    
    		/*
    		 * Not "something.*", so transform as a single expression
    		 */
    		p_target = lappend(p_target,
    						   transformTargetEntry(pstate,
    												res->val,
    												NULL,
    												res->name,
    												false));
    	}
    
    	return p_target;
    }
    
    
    /*
     * transformExpressionList()
     *
     * This is the identical transformation to transformTargetList, except that
     * the input list elements are bare expressions without ResTarget decoration,
     * and the output elements are likewise just expressions without TargetEntry
     * decoration.	We use this for ROW() and VALUES() constructs.
     */
    List *
    transformExpressionList(ParseState *pstate, List *exprlist)
    {
    	List	   *result = NIL;
    	ListCell   *lc;
    
    	foreach(lc, exprlist)
    	{
    		Node	   *e = (Node *) lfirst(lc);
    
    		/*
    		 * Check for "something.*".  Depending on the complexity of the
    		 * "something", the star could appear as the last field in ColumnRef,
    		 * or as the last indirection item in A_Indirection.
    		 */
    		if (IsA(e, ColumnRef))
    		{
    			ColumnRef  *cref = (ColumnRef *) e;
    
    			if (IsA(llast(cref->fields), A_Star))
    			{
    				/* It is something.*, expand into multiple items */
    				result = list_concat(result,
    									 ExpandColumnRefStar(pstate, cref,
    														 false));
    				continue;
    			}
    		}
    		else if (IsA(e, A_Indirection))
    		{
    			A_Indirection *ind = (A_Indirection *) e;
    
    			if (IsA(llast(ind->indirection), A_Star))
    			{
    				/* It is something.*, expand into multiple items */
    				result = list_concat(result,
    									 ExpandIndirectionStar(pstate, ind,
    														   false));
    				continue;
    			}
    		}
    
    		/*
    		 * Not "something.*", so transform as a single expression
    		 */
    		result = lappend(result,
    						 transformExpr(pstate, e));
    	}
    
    	return result;
    }
    
    
    /*
     * markTargetListOrigins()
     *		Mark targetlist columns that are simple Vars with the source
     *		table's OID and column number.
     *
     * Currently, this is done only for SELECT targetlists, since we only
     * need the info if we are going to send it to the frontend.
     */
    void
    markTargetListOrigins(ParseState *pstate, List *targetlist)
    {
    	ListCell   *l;
    
    	foreach(l, targetlist)
    	{
    		TargetEntry *tle = (TargetEntry *) lfirst(l);
    
    		markTargetListOrigin(pstate, tle, (Var *) tle->expr, 0);
    	}
    }
    
    /*
     * markTargetListOrigin()
     *		If 'var' is a Var of a plain relation, mark 'tle' with its origin
     *
     * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
     *
     * This is split out so it can recurse for join references.  Note that we
     * do not drill down into views, but report the view as the column owner.
     */
    static void
    markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
    					 Var *var, int levelsup)
    {
    	int			netlevelsup;
    	RangeTblEntry *rte;
    	AttrNumber	attnum;
    
    	if (var == NULL || !IsA(var, Var))
    		return;
    	netlevelsup = var->varlevelsup + levelsup;
    	rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup);
    	attnum = var->varattno;
    
    	switch (rte->rtekind)
    	{
    		case RTE_RELATION:
    			/* It's a table or view, report it */
    			tle->resorigtbl = rte->relid;
    			tle->resorigcol = attnum;
    			break;
    		case RTE_SUBQUERY:
    			/* Subselect-in-FROM: copy up from the subselect */
    			if (attnum != InvalidAttrNumber)
    			{
    				TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
    													attnum);
    
    				if (ste == NULL || ste->resjunk)
    					elog(ERROR, "subquery %s does not have attribute %d",
    						 rte->eref->aliasname, attnum);
    				tle->resorigtbl = ste->resorigtbl;
    				tle->resorigcol = ste->resorigcol;
    			}
    			break;
    		case RTE_JOIN:
    			/* Join RTE --- recursively inspect the alias variable */
    			if (attnum != InvalidAttrNumber)
    			{
    				Var		   *aliasvar;
    
    				Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
    				aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
    				markTargetListOrigin(pstate, tle, aliasvar, netlevelsup);
    			}
    			break;
    		case RTE_FUNCTION:
    		case RTE_VALUES:
    			/* not a simple relation, leave it unmarked */
    			break;
    		case RTE_CTE:
    
    			/*
    			 * CTE reference: copy up from the subquery, if possible. If the
    			 * RTE is a recursive self-reference then we can't do anything
    			 * because we haven't finished analyzing it yet. However, it's no
    			 * big loss because we must be down inside the recursive term of a
    			 * recursive CTE, and so any markings on the current targetlist
    			 * are not going to affect the results anyway.
    			 */
    			if (attnum != InvalidAttrNumber && !rte->self_reference)
    			{
    				CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup);
    				TargetEntry *ste;
    
    				ste = get_tle_by_resno(GetCTETargetList(cte), attnum);
    				if (ste == NULL || ste->resjunk)
    					elog(ERROR, "subquery %s does not have attribute %d",
    						 rte->eref->aliasname, attnum);
    				tle->resorigtbl = ste->resorigtbl;
    				tle->resorigcol = ste->resorigcol;
    			}
    			break;
    	}
    }
    
    
    /*
     * transformAssignedExpr()
     *	This is used in INSERT and UPDATE statements only.	It prepares an
     *	expression for assignment to a column of the target table.
     *	This includes coercing the given value to the target column's type
     *	(if necessary), and dealing with any subfield names or subscripts
     *	attached to the target column itself.  The input expression has
     *	already been through transformExpr().
     *
     * pstate		parse state
     * expr			expression to be modified
     * colname		target column name (ie, name of attribute to be assigned to)
     * attrno		target attribute number
     * indirection	subscripts/field names for target column, if any
     * location		error cursor position for the target column, or -1
     *
     * Returns the modified expression.
     *
     * Note: location points at the target column name (SET target or INSERT
     * column name list entry), and must therefore be -1 in an INSERT that
     * omits the column name list.	So we should usually prefer to use
     * exprLocation(expr) for errors that can happen in a default INSERT.
     */
    Expr *
    transformAssignedExpr(ParseState *pstate,
    					  Expr *expr,
    					  char *colname,
    					  int attrno,
    					  List *indirection,
    					  int location)
    {
    	Oid			type_id;		/* type of value provided */
    	Oid			attrtype;		/* type of target column */
    	int32		attrtypmod;
    	Oid			attrcollation;	/* collation of target column */
    	Relation	rd = pstate->p_target_relation;
    
    	Assert(rd != NULL);
    	if (attrno <= 0)
    		ereport(ERROR,
    				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    				 errmsg("cannot assign to system column \"%s\"",
    						colname),
    				 parser_errposition(pstate, location)));
    	attrtype = attnumTypeId(rd, attrno);
    	attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
    	attrcollation = rd->rd_att->attrs[attrno - 1]->attcollation;
    
    	/*
    	 * If the expression is a DEFAULT placeholder, insert the attribute's
    	 * type/typmod/collation into it so that exprType etc will report the
    	 * right things.  (We expect that the eventually substituted default
    	 * expression will in fact have this type and typmod.  The collation
    	 * likely doesn't matter, but let's set it correctly anyway.)  Also,
    	 * reject trying to update a subfield or array element with DEFAULT, since
    	 * there can't be any default for portions of a column.
    	 */
    	if (expr && IsA(expr, SetToDefault))
    	{
    		SetToDefault *def = (SetToDefault *) expr;
    
    		def->typeId = attrtype;
    		def->typeMod = attrtypmod;
    		def->collation = attrcollation;
    		if (indirection)
    		{
    			if (IsA(linitial(indirection), A_Indices))
    				ereport(ERROR,
    						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    						 errmsg("cannot set an array element to DEFAULT"),
    						 parser_errposition(pstate, location)));
    			else
    				ereport(ERROR,
    						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    						 errmsg("cannot set a subfield to DEFAULT"),
    						 parser_errposition(pstate, location)));
    		}
    	}
    
    	/* Now we can use exprType() safely. */
    	type_id = exprType((Node *) expr);
    
    	/*
    	 * If there is indirection on the target column, prepare an array or
    	 * subfield assignment expression.	This will generate a new column value
    	 * that the source value has been inserted into, which can then be placed
    	 * in the new tuple constructed by INSERT or UPDATE.
    	 */
    	if (indirection)
    	{
    		Node	   *colVar;
    
    		if (pstate->p_is_insert)
    		{
    			/*
    			 * The command is INSERT INTO table (col.something) ... so there
    			 * is not really a source value to work with. Insert a NULL
    			 * constant as the source value.
    			 */
    			colVar = (Node *) makeNullConst(attrtype, attrtypmod,
    											attrcollation);
    		}
    		else
    		{
    			/*
    			 * Build a Var for the column to be updated.
    			 */
    			colVar = (Node *) make_var(pstate,
    									   pstate->p_target_rangetblentry,
    									   attrno,
    									   location);
    		}
    
    		expr = (Expr *)
    			transformAssignmentIndirection(pstate,
    										   colVar,
    										   colname,
    										   false,
    										   attrtype,
    										   attrtypmod,
    										   attrcollation,
    										   list_head(indirection),
    										   (Node *) expr,
    										   location);
    	}
    	else
    	{
    		/*
    		 * For normal non-qualified target column, do type checking and
    		 * coercion.
    		 */
    		Node	   *orig_expr = (Node *) expr;
    
    		expr = (Expr *)
    			coerce_to_target_type(pstate,
    								  orig_expr, type_id,
    								  attrtype, attrtypmod,
    								  COERCION_ASSIGNMENT,
    								  COERCE_IMPLICIT_CAST,
    								  -1);
    		if (expr == NULL)
    			ereport(ERROR,
    					(errcode(ERRCODE_DATATYPE_MISMATCH),
    					 errmsg("column \"%s\" is of type %s"
    							" but expression is of type %s",
    							colname,
    							format_type_be(attrtype),
    							format_type_be(type_id)),
    				 errhint("You will need to rewrite or cast the expression."),
    					 parser_errposition(pstate, exprLocation(orig_expr))));
    	}
    
    	return expr;
    }
    
    
    /*
     * updateTargetListEntry()
     *	This is used in UPDATE statements only. It prepares an UPDATE
     *	TargetEntry for assignment to a column of the target table.
     *	This includes coercing the given value to the target column's type
     *	(if necessary), and dealing with any subfield names or subscripts
     *	attached to the target column itself.
     *
     * pstate		parse state
     * tle			target list entry to be modified
     * colname		target column name (ie, name of attribute to be assigned to)
     * attrno		target attribute number
     * indirection	subscripts/field names for target column, if any
     * location		error cursor position (should point at column name), or -1
     */
    void
    updateTargetListEntry(ParseState *pstate,
    					  TargetEntry *tle,
    					  char *colname,
    					  int attrno,
    					  List *indirection,
    					  int location)
    {
    	/* Fix up expression as needed */
    	tle->expr = transformAssignedExpr(pstate,
    									  tle->expr,
    									  colname,
    									  attrno,
    									  indirection,
    									  location);
    
    	/*
    	 * Set the resno to identify the target column --- the rewriter and
    	 * planner depend on this.	We also set the resname to identify the target
    	 * column, but this is only for debugging purposes; it should not be
    	 * relied on.  (In particular, it might be out of date in a stored rule.)
    	 */
    	tle->resno = (AttrNumber) attrno;
    	tle->resname = colname;
    }
    
    
    /*
     * Process indirection (field selection or subscripting) of the target
     * column in INSERT/UPDATE.  This routine recurses for multiple levels
     * of indirection --- but note that several adjacent A_Indices nodes in
     * the indirection list are treated as a single multidimensional subscript
     * operation.
     *
     * In the initial call, basenode is a Var for the target column in UPDATE,
     * or a null Const of the target's type in INSERT.  In recursive calls,
     * basenode is NULL, indicating that a substitute node should be consed up if
     * needed.
     *
     * targetName is the name of the field or subfield we're assigning to, and
     * targetIsArray is true if we're subscripting it.  These are just for
     * error reporting.
     *
     * targetTypeId, targetTypMod, targetCollation indicate the datatype and
     * collation of the object to be assigned to (initially the target column,
     * later some subobject).
     *
     * indirection is the sublist remaining to process.  When it's NULL, we're
     * done recursing and can just coerce and return the RHS.
     *
     * rhs is the already-transformed value to be assigned; note it has not been
     * coerced to any particular type.
     *
     * location is the cursor error position for any errors.  (Note: this points
     * to the head of the target clause, eg "foo" in "foo.bar[baz]".  Later we
     * might want to decorate indirection cells with their own location info,
     * in which case the location argument could probably be dropped.)
     */
    static Node *
    transformAssignmentIndirection(ParseState *pstate,
    							   Node *basenode,
    							   const char *targetName,
    							   bool targetIsArray,
    							   Oid targetTypeId,
    							   int32 targetTypMod,
    							   Oid targetCollation,
    							   ListCell *indirection,
    							   Node *rhs,
    							   int location)
    {
    	Node	   *result;
    	List	   *subscripts = NIL;
    	bool		isSlice = false;
    	ListCell   *i;
    
    	if (indirection && !basenode)
    	{
    		/* Set up a substitution.  We reuse CaseTestExpr for this. */
    		CaseTestExpr *ctest = makeNode(CaseTestExpr);
    
    		ctest->typeId = targetTypeId;
    		ctest->typeMod = targetTypMod;
    		ctest->collation = targetCollation;
    		basenode = (Node *) ctest;
    	}
    
    	/*
    	 * We have to split any field-selection operations apart from
    	 * subscripting.  Adjacent A_Indices nodes have to be treated as a single
    	 * multidimensional subscript operation.
    	 */
    	for_each_cell(i, indirection)
    	{
    		Node	   *n = lfirst(i);
    
    		if (IsA(n, A_Indices))
    		{
    			subscripts = lappend(subscripts, n);
    			if (((A_Indices *) n)->lidx != NULL)
    				isSlice = true;
    		}
    		else if (IsA(n, A_Star))
    		{
    			ereport(ERROR,
    					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    					 errmsg("row expansion via \"*\" is not supported here"),
    					 parser_errposition(pstate, location)));
    		}
    		else
    		{
    			FieldStore *fstore;
    			Oid			typrelid;
    			AttrNumber	attnum;
    			Oid			fieldTypeId;
    			int32		fieldTypMod;
    			Oid			fieldCollation;
    
    			Assert(IsA(n, String));
    
    			/* process subscripts before this field selection */
    			if (subscripts)
    			{
    				/* recurse, and then return because we're done */
    				return transformAssignmentSubscripts(pstate,
    													 basenode,
    													 targetName,
    													 targetTypeId,
    													 targetTypMod,
    													 targetCollation,
    													 subscripts,
    													 isSlice,
    													 i,
    													 rhs,
    													 location);
    			}
    
    			/* No subscripts, so can process field selection here */
    
    			typrelid = typeidTypeRelid(targetTypeId);
    			if (!typrelid)
    				ereport(ERROR,
    						(errcode(ERRCODE_DATATYPE_MISMATCH),
    						 errmsg("cannot assign to field \"%s\" of column \"%s\" because its type %s is not a composite type",
    								strVal(n), targetName,
    								format_type_be(targetTypeId)),
    						 parser_errposition(pstate, location)));
    
    			attnum = get_attnum(typrelid, strVal(n));
    			if (attnum == InvalidAttrNumber)
    				ereport(ERROR,
    						(errcode(ERRCODE_UNDEFINED_COLUMN),
    						 errmsg("cannot assign to field \"%s\" of column \"%s\" because there is no such column in data type %s",
    								strVal(n), targetName,
    								format_type_be(targetTypeId)),
    						 parser_errposition(pstate, location)));
    			if (attnum < 0)
    				ereport(ERROR,
    						(errcode(ERRCODE_UNDEFINED_COLUMN),
    						 errmsg("cannot assign to system column \"%s\"",
    								strVal(n)),
    						 parser_errposition(pstate, location)));
    
    			get_atttypetypmodcoll(typrelid, attnum,
    								&fieldTypeId, &fieldTypMod, &fieldCollation);
    
    			/* recurse to create appropriate RHS for field assign */
    			rhs = transformAssignmentIndirection(pstate,
    												 NULL,
    												 strVal(n),
    												 false,
    												 fieldTypeId,
    												 fieldTypMod,
    												 fieldCollation,
    												 lnext(i),
    												 rhs,
    												 location);
    
    			/* and build a FieldStore node */
    			fstore = makeNode(FieldStore);
    			fstore->arg = (Expr *) basenode;
    			fstore->newvals = list_make1(rhs);
    			fstore->fieldnums = list_make1_int(attnum);
    			fstore->resulttype = targetTypeId;
    
    			return (Node *) fstore;
    		}
    	}
    
    	/* process trailing subscripts, if any */
    	if (subscripts)
    	{
    		/* recurse, and then return because we're done */
    		return transformAssignmentSubscripts(pstate,
    											 basenode,
    											 targetName,
    											 targetTypeId,
    											 targetTypMod,
    											 targetCollation,
    											 subscripts,
    											 isSlice,
    											 NULL,
    											 rhs,
    											 location);
    	}
    
    	/* base case: just coerce RHS to match target type ID */
    
    	result = coerce_to_target_type(pstate,
    								   rhs, exprType(rhs),
    								   targetTypeId, targetTypMod,
    								   COERCION_ASSIGNMENT,
    								   COERCE_IMPLICIT_CAST,
    								   -1);
    	if (result == NULL)
    	{
    		if (targetIsArray)
    			ereport(ERROR,
    					(errcode(ERRCODE_DATATYPE_MISMATCH),
    					 errmsg("array assignment to \"%s\" requires type %s"
    							" but expression is of type %s",
    							targetName,
    							format_type_be(targetTypeId),
    							format_type_be(exprType(rhs))),
    				 errhint("You will need to rewrite or cast the expression."),
    					 parser_errposition(pstate, location)));
    		else
    			ereport(ERROR,
    					(errcode(ERRCODE_DATATYPE_MISMATCH),
    					 errmsg("subfield \"%s\" is of type %s"
    							" but expression is of type %s",
    							targetName,
    							format_type_be(targetTypeId),
    							format_type_be(exprType(rhs))),
    				 errhint("You will need to rewrite or cast the expression."),
    					 parser_errposition(pstate, location)));
    	}
    
    	return result;
    }
    
    /*
     * helper for transformAssignmentIndirection: process array assignment
     */
    static Node *
    transformAssignmentSubscripts(ParseState *pstate,
    							  Node *basenode,
    							  const char *targetName,
    							  Oid targetTypeId,
    							  int32 targetTypMod,
    							  Oid targetCollation,
    							  List *subscripts,
    							  bool isSlice,
    							  ListCell *next_indirection,
    							  Node *rhs,
    							  int location)
    {
    	Node	   *result;
    	Oid			arrayType;
    	int32		arrayTypMod;
    	Oid			elementTypeId;
    	Oid			typeNeeded;
    	Oid			collationNeeded;
    
    	Assert(subscripts != NIL);
    
    	/* Identify the actual array type and element type involved */
    	arrayType = targetTypeId;
    	arrayTypMod = targetTypMod;
    	elementTypeId = transformArrayType(&arrayType, &arrayTypMod);
    
    	/* Identify type that RHS must provide */
    	typeNeeded = isSlice ? arrayType : elementTypeId;
    
    	/*
    	 * Array normally has same collation as elements, but there's an
    	 * exception: we might be subscripting a domain over an array type. In
    	 * that case use collation of the base type.
    	 */
    	if (arrayType == targetTypeId)
    		collationNeeded = targetCollation;
    	else
    		collationNeeded = get_typcollation(arrayType);
    
    	/* recurse to create appropriate RHS for array assign */
    	rhs = transformAssignmentIndirection(pstate,
    										 NULL,
    										 targetName,
    										 true,
    										 typeNeeded,
    										 arrayTypMod,
    										 collationNeeded,
    										 next_indirection,
    										 rhs,
    										 location);
    
    	/* process subscripts */
    	result = (Node *) transformArraySubscripts(pstate,
    											   basenode,
    											   arrayType,
    											   elementTypeId,
    											   arrayTypMod,
    											   subscripts,
    											   rhs);
    
    	/* If target was a domain over array, need to coerce up to the domain */
    	if (arrayType != targetTypeId)
    	{
    		result = coerce_to_target_type(pstate,
    									   result, exprType(result),
    									   targetTypeId, targetTypMod,
    									   COERCION_ASSIGNMENT,
    									   COERCE_IMPLICIT_CAST,
    									   -1);
    		/* probably shouldn't fail, but check */
    		if (result == NULL)
    			ereport(ERROR,
    					(errcode(ERRCODE_CANNOT_COERCE),
    					 errmsg("cannot cast type %s to %s",
    							format_type_be(exprType(result)),
    							format_type_be(targetTypeId)),
    					 parser_errposition(pstate, location)));
    	}
    
    	return result;
    }
    
    
    /*
     * checkInsertTargets -
     *	  generate a list of INSERT column targets if not supplied, or
     *	  test supplied column names to make sure they are in target table.
     *	  Also return an integer list of the columns' attribute numbers.
     */
    List *
    checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
    {
    	*attrnos = NIL;
    
    	if (cols == NIL)
    	{
    		/*
    		 * Generate default column list for INSERT.
    		 */
    		Form_pg_attribute *attr = pstate->p_target_relation->rd_att->attrs;
    		int			numcol = pstate->p_target_relation->rd_rel->relnatts;
    		int			i;
    
    		for (i = 0; i < numcol; i++)
    		{
    			ResTarget  *col;
    
    			if (attr[i]->attisdropped)
    				continue;
    
    			col = makeNode(ResTarget);
    			col->name = pstrdup(NameStr(attr[i]->attname));
    			col->indirection = NIL;
    			col->val = NULL;
    			col->location = -1;
    			cols = lappend(cols, col);
    			*attrnos = lappend_int(*attrnos, i + 1);
    		}
    	}
    	else
    	{
    		/*
    		 * Do initial validation of user-supplied INSERT column list.
    		 */
    		Bitmapset  *wholecols = NULL;
    		Bitmapset  *partialcols = NULL;
    		ListCell   *tl;
    
    		foreach(tl, cols)
    		{
    			ResTarget  *col = (ResTarget *) lfirst(tl);
    			char	   *name = col->name;
    			int			attrno;
    
    			/* Lookup column name, ereport on failure */
    			attrno = attnameAttNum(pstate->p_target_relation, name, false);
    			if (attrno == InvalidAttrNumber)
    				ereport(ERROR,
    						(errcode(ERRCODE_UNDEFINED_COLUMN),
    					errmsg("column \"%s\" of relation \"%s\" does not exist",
    						   name,
    						 RelationGetRelationName(pstate->p_target_relation)),
    						 parser_errposition(pstate, col->location)));
    
    			/*
    			 * Check for duplicates, but only of whole columns --- we allow
    			 * INSERT INTO foo (col.subcol1, col.subcol2)
    			 */
    			if (col->indirection == NIL)
    			{
    				/* whole column; must not have any other assignment */
    				if (bms_is_member(attrno, wholecols) ||
    					bms_is_member(attrno, partialcols))
    					ereport(ERROR,
    							(errcode(ERRCODE_DUPLICATE_COLUMN),
    							 errmsg("column \"%s\" specified more than once",
    									name),
    							 parser_errposition(pstate, col->location)));
    				wholecols = bms_add_member(wholecols, attrno);
    			}
    			else
    			{
    				/* partial column; must not have any whole assignment */
    				if (bms_is_member(attrno, wholecols))
    					ereport(ERROR,
    							(errcode(ERRCODE_DUPLICATE_COLUMN),
    							 errmsg("column \"%s\" specified more than once",
    									name),
    							 parser_errposition(pstate, col->location)));
    				partialcols = bms_add_member(partialcols, attrno);
    			}
    
    			*attrnos = lappend_int(*attrnos, attrno);
    		}
    	}
    
    	return cols;
    }
    
    /*
     * ExpandColumnRefStar()
     *		Transforms foo.* into a list of expressions or targetlist entries.
     *
     * This handles the case where '*' appears as the last or only item in a
     * ColumnRef.  The code is shared between the case of foo.* at the top level
     * in a SELECT target list (where we want TargetEntry nodes in the result)
     * and foo.* in a ROW() or VALUES() construct (where we want just bare
     * expressions).
     *
     * The referenced columns are marked as requiring SELECT access.
     */
    static List *
    ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
    					bool targetlist)
    {
    	List	   *fields = cref->fields;
    	int			numnames = list_length(fields);
    
    	if (numnames == 1)
    	{
    		/*
    		 * Target item is a bare '*', expand all tables
    		 *
    		 * (e.g., SELECT * FROM emp, dept)
    		 *
    		 * Since the grammar only accepts bare '*' at top level of SELECT, we
    		 * need not handle the targetlist==false case here.
    		 */
    		Assert(targetlist);
    		return ExpandAllTables(pstate, cref->location);
    	}
    	else
    	{
    		/*
    		 * Target item is relation.*, expand that table
    		 *
    		 * (e.g., SELECT emp.*, dname FROM emp, dept)
    		 *
    		 * Note: this code is a lot like transformColumnRef; it's tempting to
    		 * call that instead and then replace the resulting whole-row Var with
    		 * a list of Vars.	However, that would leave us with the RTE's
    		 * selectedCols bitmap showing the whole row as needing select
    		 * permission, as well as the individual columns.  That would be
    		 * incorrect (since columns added later shouldn't need select
    		 * permissions).  We could try to remove the whole-row permission bit
    		 * after the fact, but duplicating code is less messy.
    		 */
    		char	   *nspname = NULL;
    		char	   *relname = NULL;
    		RangeTblEntry *rte = NULL;
    		int			levels_up;
    		enum
    		{
    			CRSERR_NO_RTE,
    			CRSERR_WRONG_DB,
    			CRSERR_TOO_MANY
    		}			crserr = CRSERR_NO_RTE;
    
    		/*
    		 * Give the PreParseColumnRefHook, if any, first shot.	If it returns
    		 * non-null then we should use that expression.
    		 */
    		if (pstate->p_pre_columnref_hook != NULL)
    		{
    			Node	   *node;
    
    			node = (*pstate->p_pre_columnref_hook) (pstate, cref);
    			if (node != NULL)
    				return ExpandRowReference(pstate, node, targetlist);
    		}
    
    		switch (numnames)
    		{
    			case 2:
    				relname = strVal(linitial(fields));
    				rte = refnameRangeTblEntry(pstate, nspname, relname,
    										   cref->location,
    										   &levels_up);
    				break;
    			case 3:
    				nspname = strVal(linitial(fields));
    				relname = strVal(lsecond(fields));
    				rte = refnameRangeTblEntry(pstate, nspname, relname,
    										   cref->location,
    										   &levels_up);
    				break;
    			case 4:
    				{
    					char	   *catname = strVal(linitial(fields));
    
    					/*
    					 * We check the catalog name and then ignore it.
    					 */
    					if (strcmp(catname, get_database_name(MyDatabaseId)) != 0)
    					{
    						crserr = CRSERR_WRONG_DB;
    						break;
    					}
    					nspname = strVal(lsecond(fields));
    					relname = strVal(lthird(fields));
    					rte = refnameRangeTblEntry(pstate, nspname, relname,
    											   cref->location,
    											   &levels_up);
    					break;
    				}
    			default:
    				crserr = CRSERR_TOO_MANY;
    				break;
    		}
    
    		/*
    		 * Now give the PostParseColumnRefHook, if any, a chance. We cheat a
    		 * bit by passing the RangeTblEntry, not a Var, as the planned
    		 * translation.  (A single Var wouldn't be strictly correct anyway.
    		 * This convention allows hooks that really care to know what is
    		 * happening.)
    		 */
    		if (pstate->p_post_columnref_hook != NULL)
    		{
    			Node	   *node;
    
    			node = (*pstate->p_post_columnref_hook) (pstate, cref,
    													 (Node *) rte);
    			if (node != NULL)
    			{
    				if (rte != NULL)
    					ereport(ERROR,
    							(errcode(ERRCODE_AMBIGUOUS_COLUMN),
    							 errmsg("column reference \"%s\" is ambiguous",
    									NameListToString(cref->fields)),
    							 parser_errposition(pstate, cref->location)));
    				return ExpandRowReference(pstate, node, targetlist);
    			}
    		}
    
    		/*
    		 * Throw error if no translation found.
    		 */
    		if (rte == NULL)
    		{
    			switch (crserr)
    			{
    				case CRSERR_NO_RTE:
    					errorMissingRTE(pstate, makeRangeVar(nspname, relname,
    														 cref->location));
    					break;
    				case CRSERR_WRONG_DB:
    					ereport(ERROR,
    							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    							 errmsg("cross-database references are not implemented: %s",
    									NameListToString(cref->fields)),
    							 parser_errposition(pstate, cref->location)));
    					break;
    				case CRSERR_TOO_MANY:
    					ereport(ERROR,
    							(errcode(ERRCODE_SYNTAX_ERROR),
    							 errmsg("improper qualified name (too many dotted names): %s",
    									NameListToString(cref->fields)),
    							 parser_errposition(pstate, cref->location)));
    					break;
    			}
    		}
    
    		/*
    		 * OK, expand the RTE into fields.
    		 */
    		return ExpandSingleTable(pstate, rte, cref->location, targetlist);
    	}
    }
    
    /*
     * ExpandAllTables()
     *		Transforms '*' (in the target list) into a list of targetlist entries.
     *
     * tlist entries are generated for each relation appearing in the query's
     * varnamespace.  We do not consider relnamespace because that would include
     * input tables of aliasless JOINs, NEW/OLD pseudo-entries, etc.
     *
     * The referenced relations/columns are marked as requiring SELECT access.
     */
    static List *
    ExpandAllTables(ParseState *pstate, int location)
    {
    	List	   *target = NIL;
    	ListCell   *l;
    
    	/* Check for SELECT *; */
    	if (!pstate->p_varnamespace)
    		ereport(ERROR,
    				(errcode(ERRCODE_SYNTAX_ERROR),
    				 errmsg("SELECT * with no tables specified is not valid"),
    				 parser_errposition(pstate, location)));
    
    	foreach(l, pstate->p_varnamespace)
    	{
    		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
    		int			rtindex = RTERangeTablePosn(pstate, rte, NULL);
    
    		target = list_concat(target,
    							 expandRelAttrs(pstate, rte, rtindex, 0,
    											location));
    	}
    
    	return target;
    }
    
    /*
     * ExpandIndirectionStar()
     *		Transforms foo.* into a list of expressions or targetlist entries.
     *
     * This handles the case where '*' appears as the last item in A_Indirection.
     * The code is shared between the case of foo.* at the top level in a SELECT
     * target list (where we want TargetEntry nodes in the result) and foo.* in
     * a ROW() or VALUES() construct (where we want just bare expressions).
     */
    static List *
    ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
    					  bool targetlist)
    {
    	Node	   *expr;
    
    	/* Strip off the '*' to create a reference to the rowtype object */
    	ind = copyObject(ind);
    	ind->indirection = list_truncate(ind->indirection,
    									 list_length(ind->indirection) - 1);
    
    	/* And transform that */
    	expr = transformExpr(pstate, (Node *) ind);
    
    	/* Expand the rowtype expression into individual fields */
    	return ExpandRowReference(pstate, expr, targetlist);
    }
    
    /*
     * ExpandSingleTable()
     *		Transforms foo.* into a list of expressions or targetlist entries.
     *
     * This handles the case where foo has been determined to be a simple
     * reference to an RTE, so we can just generate Vars for the expressions.
     *
     * The referenced columns are marked as requiring SELECT access.
     */
    static List *
    ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
    				  int location, bool targetlist)
    {
    	int			sublevels_up;
    	int			rtindex;
    
    	rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
    
    	if (targetlist)
    	{
    		/* expandRelAttrs handles permissions marking */
    		return expandRelAttrs(pstate, rte, rtindex, sublevels_up,
    							  location);
    	}
    	else
    	{
    		List	   *vars;
    		ListCell   *l;
    
    		expandRTE(rte, rtindex, sublevels_up, location, false,
    				  NULL, &vars);
    
    		/*
    		 * Require read access to the table.  This is normally redundant with
    		 * the markVarForSelectPriv calls below, but not if the table has zero
    		 * columns.
    		 */
    		rte->requiredPerms |= ACL_SELECT;
    
    		/* Require read access to each column */
    		foreach(l, vars)
    		{
    			Var		   *var = (Var *) lfirst(l);
    
    			markVarForSelectPriv(pstate, var, rte);
    		}
    
    		return vars;
    	}
    }
    
    /*
     * ExpandRowReference()
     *		Transforms foo.* into a list of expressions or targetlist entries.
     *
     * This handles the case where foo is an arbitrary expression of composite
     * type.
     */
    static List *
    ExpandRowReference(ParseState *pstate, Node *expr,
    				   bool targetlist)
    {
    	List	   *result = NIL;
    	TupleDesc	tupleDesc;
    	int			numAttrs;
    	int			i;
    
    	/*
    	 * If the rowtype expression is a whole-row Var, we can expand the fields
    	 * as simple Vars.	Note: if the RTE is a relation, this case leaves us
    	 * with the RTE's selectedCols bitmap showing the whole row as needing
    	 * select permission, as well as the individual columns.  However, we can
    	 * only get here for weird notations like (table.*).*, so it's not worth
    	 * trying to clean up --- arguably, the permissions marking is correct
    	 * anyway for such cases.
    	 */
    	if (IsA(expr, Var) &&
    		((Var *) expr)->varattno == InvalidAttrNumber)
    	{
    		Var		   *var = (Var *) expr;
    		RangeTblEntry *rte;
    
    		rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
    		return ExpandSingleTable(pstate, rte, var->location, targetlist);
    	}
    
    	/*
    	 * Otherwise we have to do it the hard way.  Our current implementation is
    	 * to generate multiple copies of the expression and do FieldSelects.
    	 * (This can be pretty inefficient if the expression involves nontrivial
    	 * computation :-(.)
    	 *
    	 * Verify it's a composite type, and get the tupdesc.  We use
    	 * get_expr_result_type() because that can handle references to functions
    	 * returning anonymous record types.  If that fails, use
    	 * lookup_rowtype_tupdesc(), which will almost certainly fail as well, but
    	 * it will give an appropriate error message.
    	 *
    	 * If it's a Var of type RECORD, we have to work even harder: we have to
    	 * find what the Var refers to, and pass that to get_expr_result_type.
    	 * That task is handled by expandRecordVariable().
    	 */
    	if (IsA(expr, Var) &&
    		((Var *) expr)->vartype == RECORDOID)
    		tupleDesc = expandRecordVariable(pstate, (Var *) expr, 0);
    	else if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
    		tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
    												exprTypmod(expr));
    	Assert(tupleDesc);
    
    	/* Generate a list of references to the individual fields */
    	numAttrs = tupleDesc->natts;
    	for (i = 0; i < numAttrs; i++)
    	{
    		Form_pg_attribute att = tupleDesc->attrs[i];
    		FieldSelect *fselect;
    
    		if (att->attisdropped)
    			continue;
    
    		fselect = makeNode(FieldSelect);
    		fselect->arg = (Expr *) copyObject(expr);
    		fselect->fieldnum = i + 1;
    		fselect->resulttype = att->atttypid;
    		fselect->resulttypmod = att->atttypmod;
    		/* save attribute's collation for parse_collate.c */
    		fselect->resultcollid = att->attcollation;
    
    		if (targetlist)
    		{
    			/* add TargetEntry decoration */
    			TargetEntry *te;
    
    			te = makeTargetEntry((Expr *) fselect,
    								 (AttrNumber) pstate->p_next_resno++,
    								 pstrdup(NameStr(att->attname)),
    								 false);
    			result = lappend(result, te);
    		}
    		else
    			result = lappend(result, fselect);
    	}
    
    	return result;
    }
    
    /*
     * expandRecordVariable
     *		Get the tuple descriptor for a Var of type RECORD, if possible.
     *
     * Since no actual table or view column is allowed to have type RECORD, such
     * a Var must refer to a JOIN or FUNCTION RTE or to a subquery output.	We
     * drill down to find the ultimate defining expression and attempt to infer
     * the tupdesc from it.  We ereport if we can't determine the tupdesc.
     *
     * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
     */
    TupleDesc
    expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
    {
    	TupleDesc	tupleDesc;
    	int			netlevelsup;
    	RangeTblEntry *rte;
    	AttrNumber	attnum;
    	Node	   *expr;
    
    	/* Check my caller didn't mess up */
    	Assert(IsA(var, Var));
    	Assert(var->vartype == RECORDOID);
    
    	netlevelsup = var->varlevelsup + levelsup;
    	rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup);
    	attnum = var->varattno;
    
    	if (attnum == InvalidAttrNumber)
    	{
    		/* Whole-row reference to an RTE, so expand the known fields */
    		List	   *names,
    				   *vars;
    		ListCell   *lname,
    				   *lvar;
    		int			i;
    
    		expandRTE(rte, var->varno, 0, var->location, false,
    				  &names, &vars);
    
    		tupleDesc = CreateTemplateTupleDesc(list_length(vars), false);
    		i = 1;
    		forboth(lname, names, lvar, vars)
    		{
    			char	   *label = strVal(lfirst(lname));
    			Node	   *varnode = (Node *) lfirst(lvar);
    
    			TupleDescInitEntry(tupleDesc, i,
    							   label,
    							   exprType(varnode),
    							   exprTypmod(varnode),
    							   0);
    			TupleDescInitEntryCollation(tupleDesc, i,
    										exprCollation(varnode));
    			i++;
    		}
    		Assert(lname == NULL && lvar == NULL);	/* lists same length? */
    
    		return tupleDesc;
    	}
    
    	expr = (Node *) var;		/* default if we can't drill down */
    
    	switch (rte->rtekind)
    	{
    		case RTE_RELATION:
    		case RTE_VALUES:
    
    			/*
    			 * This case should not occur: a column of a table or values list
    			 * shouldn't have type RECORD.  Fall through and fail (most
    			 * likely) at the bottom.
    			 */
    			break;
    		case RTE_SUBQUERY:
    			{
    				/* Subselect-in-FROM: examine sub-select's output expr */
    				TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
    													attnum);
    
    				if (ste == NULL || ste->resjunk)
    					elog(ERROR, "subquery %s does not have attribute %d",
    						 rte->eref->aliasname, attnum);
    				expr = (Node *) ste->expr;
    				if (IsA(expr, Var))
    				{
    					/*
    					 * Recurse into the sub-select to see what its Var refers
    					 * to.	We have to build an additional level of ParseState
    					 * to keep in step with varlevelsup in the subselect.
    					 */
    					ParseState	mypstate;
    
    					MemSet(&mypstate, 0, sizeof(mypstate));
    					mypstate.parentParseState = pstate;
    					mypstate.p_rtable = rte->subquery->rtable;
    					/* don't bother filling the rest of the fake pstate */
    
    					return expandRecordVariable(&mypstate, (Var *) expr, 0);
    				}
    				/* else fall through to inspect the expression */
    			}
    			break;
    		case RTE_JOIN:
    			/* Join RTE --- recursively inspect the alias variable */
    			Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
    			expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
    			if (IsA(expr, Var))
    				return expandRecordVariable(pstate, (Var *) expr, netlevelsup);
    			/* else fall through to inspect the expression */
    			break;
    		case RTE_FUNCTION:
    
    			/*
    			 * We couldn't get here unless a function is declared with one of
    			 * its result columns as RECORD, which is not allowed.
    			 */
    			break;
    		case RTE_CTE:
    			/* CTE reference: examine subquery's output expr */
    			if (!rte->self_reference)
    			{
    				CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup);
    				TargetEntry *ste;
    
    				ste = get_tle_by_resno(GetCTETargetList(cte), attnum);
    				if (ste == NULL || ste->resjunk)
    					elog(ERROR, "subquery %s does not have attribute %d",
    						 rte->eref->aliasname, attnum);
    				expr = (Node *) ste->expr;
    				if (IsA(expr, Var))
    				{
    					/*
    					 * Recurse into the CTE to see what its Var refers to. We
    					 * have to build an additional level of ParseState to keep
    					 * in step with varlevelsup in the CTE; furthermore it
    					 * could be an outer CTE.
    					 */
    					ParseState	mypstate;
    					Index		levelsup;
    
    					MemSet(&mypstate, 0, sizeof(mypstate));
    					/* this loop must work, since GetCTEForRTE did */
    					for (levelsup = 0;
    						 levelsup < rte->ctelevelsup + netlevelsup;
    						 levelsup++)
    						pstate = pstate->parentParseState;
    					mypstate.parentParseState = pstate;
    					mypstate.p_rtable = ((Query *) cte->ctequery)->rtable;
    					/* don't bother filling the rest of the fake pstate */
    
    					return expandRecordVariable(&mypstate, (Var *) expr, 0);
    				}
    				/* else fall through to inspect the expression */
    			}
    			break;
    	}
    
    	/*
    	 * We now have an expression we can't expand any more, so see if
    	 * get_expr_result_type() can do anything with it.	If not, pass to
    	 * lookup_rowtype_tupdesc() which will probably fail, but will give an
    	 * appropriate error message while failing.
    	 */
    	if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
    		tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
    												exprTypmod(expr));
    
    	return tupleDesc;
    }
    
    
    /*
     * FigureColname -
     *	  if the name of the resulting column is not specified in the target
     *	  list, we have to guess a suitable name.  The SQL spec provides some
     *	  guidance, but not much...
     *
     * Note that the argument is the *untransformed* parse tree for the target
     * item.  This is a shade easier to work with than the transformed tree.
     */
    char *
    FigureColname(Node *node)
    {
    	char	   *name = NULL;
    
    	(void) FigureColnameInternal(node, &name);
    	if (name != NULL)
    		return name;
    	/* default result if we can't guess anything */
    	return "?column?";
    }
    
    /*
     * FigureIndexColname -
     *	  choose the name for an expression column in an index
     *
     * This is actually just like FigureColname, except we return NULL if
     * we can't pick a good name.
     */
    char *
    FigureIndexColname(Node *node)
    {
    	char	   *name = NULL;
    
    	(void) FigureColnameInternal(node, &name);
    	return name;
    }
    
    /*
     * FigureColnameInternal -
     *	  internal workhorse for FigureColname
     *
     * Return value indicates strength of confidence in result:
     *		0 - no information
     *		1 - second-best name choice
     *		2 - good name choice
     * The return value is actually only used internally.
     * If the result isn't zero, *name is set to the chosen name.
     */
    static int
    FigureColnameInternal(Node *node, char **name)
    {
    	int			strength = 0;
    
    	if (node == NULL)
    		return strength;
    
    	switch (nodeTag(node))
    	{
    		case T_ColumnRef:
    			{
    				char	   *fname = NULL;
    				ListCell   *l;
    
    				/* find last field name, if any, ignoring "*" */
    				foreach(l, ((ColumnRef *) node)->fields)
    				{
    					Node	   *i = lfirst(l);
    
    					if (IsA(i, String))
    						fname = strVal(i);
    				}
    				if (fname)
    				{
    					*name = fname;
    					return 2;
    				}
    			}
    			break;
    		case T_A_Indirection:
    			{
    				A_Indirection *ind = (A_Indirection *) node;
    				char	   *fname = NULL;
    				ListCell   *l;
    
    				/* find last field name, if any, ignoring "*" and subscripts */
    				foreach(l, ind->indirection)
    				{
    					Node	   *i = lfirst(l);
    
    					if (IsA(i, String))
    						fname = strVal(i);
    				}
    				if (fname)
    				{
    					*name = fname;
    					return 2;
    				}
    				return FigureColnameInternal(ind->arg, name);
    			}
    			break;
    		case T_FuncCall:
    			*name = strVal(llast(((FuncCall *) node)->funcname));
    			return 2;
    		case T_A_Expr:
    			/* make nullif() act like a regular function */
    			if (((A_Expr *) node)->kind == AEXPR_NULLIF)
    			{
    				*name = "nullif";
    				return 2;
    			}
    			break;
    		case T_TypeCast:
    			strength = FigureColnameInternal(((TypeCast *) node)->arg,
    											 name);
    			if (strength <= 1)
    			{
    				if (((TypeCast *) node)->typeName != NULL)
    				{
    					*name = strVal(llast(((TypeCast *) node)->typeName->names));
    					return 1;
    				}
    			}
    			break;
    		case T_CollateClause:
    			return FigureColnameInternal(((CollateClause *) node)->arg, name);
    		case T_SubLink:
    			switch (((SubLink *) node)->subLinkType)
    			{
    				case EXISTS_SUBLINK:
    					*name = "exists";
    					return 2;
    				case ARRAY_SUBLINK:
    					*name = "array";
    					return 2;
    				case EXPR_SUBLINK:
    					{
    						/* Get column name of the subquery's single target */
    						SubLink	   *sublink = (SubLink *) node;
    						Query	   *query = (Query *) sublink->subselect;
    
    						/*
    						 * The subquery has probably already been transformed,
    						 * but let's be careful and check that.  (The reason
    						 * we can see a transformed subquery here is that
    						 * transformSubLink is lazy and modifies the SubLink
    						 * node in-place.)
    						 */
    						if (IsA(query, Query))
    						{
    							TargetEntry *te = (TargetEntry *) linitial(query->targetList);
    
    							if (te->resname)
    							{
    								*name = te->resname;
    								return 2;
    							}
    						}
    					}
    					break;
    				/* As with other operator-like nodes, these have no names */
    				case ALL_SUBLINK:
    				case ANY_SUBLINK:
    				case ROWCOMPARE_SUBLINK:
    				case CTE_SUBLINK:
    					break;
    			}
    			break;
    		case T_CaseExpr:
    			strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult,
    											 name);
    			if (strength <= 1)
    			{
    				*name = "case";
    				return 1;
    			}
    			break;
    		case T_A_ArrayExpr:
    			/* make ARRAY[] act like a function */
    			*name = "array";
    			return 2;
    		case T_RowExpr:
    			/* make ROW() act like a function */
    			*name = "row";
    			return 2;
    		case T_CoalesceExpr:
    			/* make coalesce() act like a regular function */
    			*name = "coalesce";
    			return 2;
    		case T_MinMaxExpr:
    			/* make greatest/least act like a regular function */
    			switch (((MinMaxExpr *) node)->op)
    			{
    				case IS_GREATEST:
    					*name = "greatest";
    					return 2;
    				case IS_LEAST:
    					*name = "least";
    					return 2;
    			}
    			break;
    		case T_XmlExpr:
    			/* make SQL/XML functions act like a regular function */
    			switch (((XmlExpr *) node)->op)
    			{
    				case IS_XMLCONCAT:
    					*name = "xmlconcat";
    					return 2;
    				case IS_XMLELEMENT:
    					*name = "xmlelement";
    					return 2;
    				case IS_XMLFOREST:
    					*name = "xmlforest";
    					return 2;
    				case IS_XMLPARSE:
    					*name = "xmlparse";
    					return 2;
    				case IS_XMLPI:
    					*name = "xmlpi";
    					return 2;
    				case IS_XMLROOT:
    					*name = "xmlroot";
    					return 2;
    				case IS_XMLSERIALIZE:
    					*name = "xmlserialize";
    					return 2;
    				case IS_DOCUMENT:
    					/* nothing */
    					break;
    			}
    			break;
    		case T_XmlSerialize:
    			*name = "xmlserialize";
    			return 2;
    		default:
    			break;
    	}
    
    	return strength;
    }