/*-------------------------------------------------------------------------
 *
 * parse_target.c
 *	  handle target lists
 *
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.129 2005/01/13 17:19:09 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.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/typcache.h"


static void markTargetListOrigin(ParseState *pstate, Resdom *res,
								 Var *var, int levelsup);
static Node *transformAssignmentIndirection(ParseState *pstate,
							   Node *basenode,
							   const char *targetName,
							   bool targetIsArray,
							   Oid targetTypeId,
							   int32 targetTypMod,
							   ListCell *indirection,
							   Node *rhs);
static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref);
static List *ExpandAllTables(ParseState *pstate);
static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind);
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)
{
	Oid			type_id;
	int32		type_mod;
	Resdom	   *resnode;

	/* Transform the node if caller didn't do it already */
	if (expr == NULL)
		expr = transformExpr(pstate, node);

	type_id = exprType(expr);
	type_mod = exprTypmod(expr);

	if (colname == NULL && !resjunk)
	{
		/*
		 * Generate a suitable column name for a column without any
		 * explicit 'AS ColumnName' clause.
		 */
		colname = FigureColname(node);
	}

	resnode = makeResdom((AttrNumber) pstate->p_next_resno++,
						 type_id,
						 type_mod,
						 colname,
						 resjunk);

	return makeTargetEntry(resnode, (Expr *) expr);
}


/*
 * 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 name in
		 * ColumnRef, or as the last indirection item in A_Indirection.
		 */
		if (IsA(res->val, ColumnRef))
		{
			ColumnRef  *cref = (ColumnRef *) res->val;

			if (strcmp(strVal(llast(cref->fields)), "*") == 0)
			{
				/* It is something.*, expand into multiple items */
				p_target = list_concat(p_target,
									   ExpandColumnRefStar(pstate, cref));
				continue;
			}
		}
		else if (IsA(res->val, A_Indirection))
		{
			A_Indirection *ind = (A_Indirection *) res->val;
			Node	   *lastitem = llast(ind->indirection);

			if (IsA(lastitem, String) &&
				strcmp(strVal(lastitem), "*") == 0)
			{
				/* It is something.*, expand into multiple items */
				p_target = list_concat(p_target,
									 ExpandIndirectionStar(pstate, ind));
				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;
}


/*
 * 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->resdom, (Var *) tle->expr, 0);
	}
}

/*
 * markTargetListOrigin()
 *		If 'var' is a Var of a plain relation, mark 'res' 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, Resdom *res, 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 */
			res->resorigtbl = rte->relid;
			res->resorigcol = attnum;
			break;
		case RTE_SUBQUERY:
			{
				/* Subselect-in-FROM: copy up from the subselect */
				TargetEntry *te = get_tle_by_resno(rte->subquery->targetList,
												   attnum);

				if (te == NULL || te->resdom->resjunk)
					elog(ERROR, "subquery %s does not have attribute %d",
						 rte->eref->aliasname, attnum);
				res->resorigtbl = te->resdom->resorigtbl;
				res->resorigcol = te->resdom->resorigcol;
			}
			break;
		case RTE_JOIN:
			{
				/* Join RTE --- recursively inspect the alias variable */
				Var		   *aliasvar;

				Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
				aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
				markTargetListOrigin(pstate, res, aliasvar, netlevelsup);
			}
			break;
		case RTE_SPECIAL:
		case RTE_FUNCTION:
			/* not a simple relation, leave it unmarked */
			break;
	}
}


/*
 * updateTargetListEntry()
 *	This is used in INSERT and UPDATE statements only.	It prepares a
 *	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
 */
void
updateTargetListEntry(ParseState *pstate,
					  TargetEntry *tle,
					  char *colname,
					  int attrno,
					  List *indirection)
{
	Oid			type_id;		/* type of value provided */
	Oid			attrtype;		/* type of target column */
	int32		attrtypmod;
	Resdom	   *resnode = tle->resdom;
	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)));
	attrtype = attnumTypeId(rd, attrno);
	attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;

	/*
	 * If the expression is a DEFAULT placeholder, insert the attribute's
	 * type/typmod into it so that exprType will report the right things.
	 * (We expect that the eventually substituted default expression will
	 * in fact have this type and typmod.)	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 (tle->expr && IsA(tle->expr, SetToDefault))
	{
		SetToDefault *def = (SetToDefault *) tle->expr;

		def->typeId = attrtype;
		def->typeMod = attrtypmod;
		if (indirection)
		{
			if (IsA(linitial(indirection), A_Indices))
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					  errmsg("cannot set an array element to DEFAULT")));
			else
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("cannot set a subfield to DEFAULT")));
		}
	}

	/* Now we can use exprType() safely. */
	type_id = exprType((Node *) tle->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);
		}
		else
		{
			/*
			 * Build a Var for the column to be updated.
			 */
			colVar = (Node *) make_var(pstate,
									   pstate->p_target_rangetblentry,
									   attrno);
		}

		tle->expr = (Expr *)
			transformAssignmentIndirection(pstate,
										   colVar,
										   colname,
										   false,
										   attrtype,
										   attrtypmod,
										   list_head(indirection),
										   (Node *) tle->expr);
	}
	else
	{
		/*
		 * For normal non-qualified target column, do type checking and
		 * coercion.
		 */
		tle->expr = (Expr *)
			coerce_to_target_type(pstate,
								  (Node *) tle->expr, type_id,
								  attrtype, attrtypmod,
								  COERCION_ASSIGNMENT,
								  COERCE_IMPLICIT_CAST);
		if (tle->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.")));
	}

	/*
	 * The result of the target expression should now match the
	 * destination column's type.
	 */
	resnode->restype = attrtype;
	resnode->restypmod = attrtypmod;

	/*
	 * 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.)
	 */
	resnode->resno = (AttrNumber) attrno;
	resnode->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 and targetTypMod indicate the datatype 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.
 */
static Node *
transformAssignmentIndirection(ParseState *pstate,
							   Node *basenode,
							   const char *targetName,
							   bool targetIsArray,
							   Oid targetTypeId,
							   int32 targetTypMod,
							   ListCell *indirection,
							   Node *rhs)
{
	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;
		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
		{
			FieldStore *fstore;
			Oid			typrelid;
			AttrNumber	attnum;
			Oid			fieldTypeId;
			int32		fieldTypMod;

			Assert(IsA(n, String));

			/* process subscripts before this field selection */
			if (subscripts)
			{
				Oid			elementTypeId = transformArrayType(targetTypeId);
				Oid			typeNeeded = isSlice ? targetTypeId : elementTypeId;

				/* recurse to create appropriate RHS for array assign */
				rhs = transformAssignmentIndirection(pstate,
													 NULL,
													 targetName,
													 true,
													 typeNeeded,
													 targetTypMod,
													 i,
													 rhs);
				/* process subscripts */
				return (Node *) transformArraySubscripts(pstate,
														 basenode,
														 targetTypeId,
														 elementTypeId,
														 targetTypMod,
														 subscripts,
														 rhs);
			}

			/* 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))));

			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))));
			if (attnum < 0)
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_COLUMN),
						 errmsg("cannot assign to system column \"%s\"",
								strVal(n))));

			get_atttypetypmod(typrelid, attnum,
							  &fieldTypeId, &fieldTypMod);

			/* recurse to create appropriate RHS for field assign */
			rhs = transformAssignmentIndirection(pstate,
												 NULL,
												 strVal(n),
												 false,
												 fieldTypeId,
												 fieldTypMod,
												 lnext(i),
												 rhs);

			/* 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)
	{
		Oid			elementTypeId = transformArrayType(targetTypeId);
		Oid			typeNeeded = isSlice ? targetTypeId : elementTypeId;

		/* recurse to create appropriate RHS for array assign */
		rhs = transformAssignmentIndirection(pstate,
											 NULL,
											 targetName,
											 true,
											 typeNeeded,
											 targetTypMod,
											 NULL,
											 rhs);
		/* process subscripts */
		return (Node *) transformArraySubscripts(pstate,
												 basenode,
												 targetTypeId,
												 elementTypeId,
												 targetTypMod,
												 subscripts,
												 rhs);
	}

	/* 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);
	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.")));
		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.")));
	}

	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;
			cols = lappend(cols, col);
			*attrnos = lappend_int(*attrnos, i + 1);
		}
	}
	else
	{
		/*
		 * Do initial validation of user-supplied INSERT column list.
		 */
		List	   *wholecols = NIL;
		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);

			/*
			 * 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 (list_member_int(*attrnos, attrno))
					ereport(ERROR,
							(errcode(ERRCODE_DUPLICATE_COLUMN),
						 errmsg("column \"%s\" specified more than once",
								name)));
				wholecols = lappend_int(wholecols, attrno);
			}
			else
			{
				/* partial column; must not have any whole assignment */
				if (list_member_int(wholecols, attrno))
					ereport(ERROR,
							(errcode(ERRCODE_DUPLICATE_COLUMN),
						 errmsg("column \"%s\" specified more than once",
								name)));
			}

			*attrnos = lappend_int(*attrnos, attrno);
		}
	}

	return cols;
}

/*
 * ExpandColumnRefStar()
 *		Turns foo.* (in the target list) into a list of targetlist entries.
 *
 * This handles the case where '*' appears as the last or only name in a
 * ColumnRef.
 */
static List *
ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref)
{
	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)
		 */
		return ExpandAllTables(pstate);
	}
	else
	{
		/*
		 * Target item is relation.*, expand that table
		 *
		 * (e.g., SELECT emp.*, dname FROM emp, dept)
		 */
		char	   *schemaname;
		char	   *relname;
		RangeTblEntry *rte;
		int			sublevels_up;
		int			rtindex;
		List	   *rtable;

		switch (numnames)
		{
			case 2:
				schemaname = NULL;
				relname = strVal(linitial(fields));
				break;
			case 3:
				schemaname = strVal(linitial(fields));
				relname = strVal(lsecond(fields));
				break;
			case 4:
				{
					char	   *name1 = strVal(linitial(fields));

					/*
					 * We check the catalog name and then ignore it.
					 */
					if (strcmp(name1, get_database_name(MyDatabaseId)) != 0)
						ereport(ERROR,
								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
								 errmsg("cross-database references are not implemented: %s",
										NameListToString(fields))));
					schemaname = strVal(lsecond(fields));
					relname = strVal(lthird(fields));
					break;
				}
			default:
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("improper qualified name (too many dotted names): %s",
								NameListToString(fields))));
				schemaname = NULL;		/* keep compiler quiet */
				relname = NULL;
				break;
		}

		rte = refnameRangeTblEntry(pstate, schemaname, relname,
								   &sublevels_up);
		if (rte == NULL)
			rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
													  relname));

		rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
		rtable = GetLevelNRangeTable(pstate, sublevels_up);

		return expandRelAttrs(pstate, rtable, rtindex, sublevels_up);
	}
}

/*
 * ExpandAllTables()
 *		Turns '*' (in the target list) into a list of targetlist entries.
 *
 * tlist entries are generated for each relation appearing at the top level
 * of the query's namespace, except for RTEs marked not inFromCl.  (These
 * may include NEW/OLD pseudo-entries, implicit RTEs, etc.)
 */
static List *
ExpandAllTables(ParseState *pstate)
{
	List	   *target = NIL;
	bool		found_table = false;
	ListCell   *ns;

	foreach(ns, pstate->p_namespace)
	{
		Node	   *n = (Node *) lfirst(ns);
		int			rtindex;
		RangeTblEntry *rte;

		if (IsA(n, RangeTblRef))
			rtindex = ((RangeTblRef *) n)->rtindex;
		else if (IsA(n, JoinExpr))
			rtindex = ((JoinExpr *) n)->rtindex;
		else
		{
			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n));
			rtindex = 0;		/* keep compiler quiet */
		}

		/*
		 * Ignore added-on relations that were not listed in the FROM
		 * clause.
		 */
		rte = rt_fetch(rtindex, pstate->p_rtable);
		if (!rte->inFromCl)
			continue;

		found_table = true;
		target = list_concat(target,
							 expandRelAttrs(pstate, pstate->p_rtable,
											rtindex, 0));
	}

	/* Check for SELECT *; */
	if (!found_table)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
			  errmsg("SELECT * with no tables specified is not valid")));

	return target;
}

/*
 * ExpandIndirectionStar()
 *		Turns foo.* (in the target list) into a list of targetlist entries.
 *
 * This handles the case where '*' appears as the last item in A_Indirection.
 */
static List *
ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind)
{
	Node	   *expr;
	TupleDesc	tupleDesc;
	int			numAttrs;
	int			i;
	List	   *te_list = NIL;

	/* 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);

	/* Verify it's a composite type, and get the tupdesc */
	tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr));

	/* 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];
		Node	   *fieldnode;
		TargetEntry *te;

		if (att->attisdropped)
			continue;

		/*
		 * If we got a whole-row Var from the rowtype reference, we can
		 * expand the fields as simple Vars.  Otherwise we must generate
		 * multiple copies of the rowtype reference and do FieldSelects.
		 */
		if (IsA(expr, Var) &&
			((Var *) expr)->varattno == InvalidAttrNumber)
		{
			Var		   *var = (Var *) expr;

			fieldnode = (Node *) makeVar(var->varno,
										 i + 1,
										 att->atttypid,
										 att->atttypmod,
										 var->varlevelsup);
		}
		else
		{
			FieldSelect *fselect = makeNode(FieldSelect);

			fselect->arg = (Expr *) copyObject(expr);
			fselect->fieldnum = i + 1;
			fselect->resulttype = att->atttypid;
			fselect->resulttypmod = att->atttypmod;

			fieldnode = (Node *) fselect;
		}

		te = makeNode(TargetEntry);
		te->resdom = makeResdom((AttrNumber) pstate->p_next_resno++,
								att->atttypid,
								att->atttypmod,
								pstrdup(NameStr(att->attname)),
								false);
		te->expr = (Expr *) fieldnode;
		te_list = lappend(te_list, te);
	}

	return te_list;
}

/*
 * 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;

	FigureColnameInternal(node, &name);
	if (name != NULL)
		return name;
	/* default result if we can't guess anything */
	return "?column?";
}

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 (strcmp(strVal(i), "*") != 0)
						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 "*" */
				foreach(l, ind->indirection)
				{
					Node	   *i = lfirst(l);

					if (IsA(i, String) &&
						strcmp(strVal(i), "*") != 0)
						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_A_Const:
			if (((A_Const *) node)->typename != NULL)
			{
				*name = strVal(llast(((A_Const *) node)->typename->names));
				return 1;
			}
			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_CaseExpr:
			strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult,
											 name);
			if (strength <= 1)
			{
				*name = "case";
				return 1;
			}
			break;
		case T_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;
		default:
			break;
	}

	return strength;
}
