/*-------------------------------------------------------------------------
 *
 * execTuples.c
 *	  Routines dealing with the executor tuple tables.	These are used to
 *	  ensure that the executor frees copies of tuples (made by
 *	  ExecTargetList) properly.
 *
 *	  Routines dealing with the type information for tuples. Currently,
 *	  the type information for a tuple is an array of FormData_pg_attribute.
 *	  This information is needed by routines manipulating tuples
 *	  (getattribute, formtuple, etc.).
 *
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /home/projects/pgsql/cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.37 2000/04/12 17:15:08 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
/*
 * INTERFACE ROUTINES
 *
 *	 TABLE CREATE/DELETE
 *		ExecCreateTupleTable	- create a new tuple table
 *		ExecDropTupleTable	- destroy a table
 *
 *	 SLOT RESERVERATION
 *		ExecAllocTableSlot		- find an available slot in the table
 *
 *	 SLOT ACCESSORS
 *		ExecStoreTuple			- store a tuple in the table
 *		ExecFetchTuple			- fetch a tuple from the table
 *		ExecClearTuple			- clear contents of a table slot
 *		ExecSlotPolicy			- return slot's tuple pfree policy
 *		ExecSetSlotPolicy		- diddle the slot policy
 *		ExecSlotDescriptor		- type of tuple in a slot
 *		ExecSetSlotDescriptor	- set a slot's tuple descriptor
 *		ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag
 *		ExecSetNewSlotDescriptor - set a desc and the is-new-flag all at once
 *
 *	 SLOT STATUS PREDICATES
 *		TupIsNull				- true when slot contains no tuple(Macro)
 *		ExecSlotDescriptorIsNew - true if we're now storing a different
 *								  type of tuple in a slot
 *
 *	 CONVENIENCE INITIALIZATION ROUTINES
 *		ExecInitResultTupleSlot    \	convience routines to initialize
 *		ExecInitScanTupleSlot		\	the various tuple slots for nodes
 *		ExecInitMarkedTupleSlot		/  which store copies of tuples.
 *		ExecInitOuterTupleSlot	   /
 *		ExecInitHashTupleSlot	  /
 *
 *	 old routines:
 *		ExecGetTupType			- get type of tuple returned by this node
 *		ExecTypeFromTL			- form a TupleDesc from a target list
 *
 *	 EXAMPLE OF HOW TABLE ROUTINES WORK
 *		Suppose we have a query such as retrieve (EMP.name) and we have
 *		a single SeqScan node in the query plan.
 *
 *		At ExecStart()
 *		----------------
 *		- InitPlan() calls ExecCreateTupleTable() to create the tuple
 *		  table which will hold tuples processed by the executor.
 *
 *		- ExecInitSeqScan() calls ExecInitScanTupleSlot() and
 *		  ExecInitResultTupleSlot() to reserve places in the tuple
 *		  table for the tuples returned by the access methods and the
 *		  tuples resulting from preforming target list projections.
 *
 *		During ExecRun()
 *		----------------
 *		- SeqNext() calls ExecStoreTuple() to place the tuple returned
 *		  by the access methods into the scan tuple slot.
 *
 *		- ExecSeqScan() calls ExecStoreTuple() to take the result
 *		  tuple from ExecTargetList() and place it into the result tuple
 *		  slot.
 *
 *		- ExecutePlan() calls ExecRetrieve() which gets the tuple out of
 *		  the slot passed to it by calling ExecFetchTuple().  this tuple
 *		  is then returned.
 *
 *		At ExecEnd()
 *		----------------
 *		- EndPlan() calls ExecDropTupleTable() to clean up any remaining
 *		  tuples left over from executing the query.
 *
 *		The important thing to watch in the executor code is how pointers
 *		to the slots containing tuples are passed instead of the tuples
 *		themselves.  This facilitates the communication of related information
 *		(such as whether or not a tuple should be pfreed, what buffer contains
 *		this tuple, the tuple's tuple descriptor, etc).   Note that much of
 *		this information is also kept in the ExprContext of each node.
 *		Soon the executor will be redesigned and ExprContext's will contain
 *		only slot pointers.  -cim 3/14/91
 *
 *	 NOTES
 *		The tuple table stuff is relatively new, put here to alleviate
 *		the process growth problems in the executor.  The other routines
 *		are old (from the original lisp system) and may someday become
 *		obsolete.  -cim 6/23/90
 *
 *		In the implementation of nested-dot queries such as
 *		"retrieve (EMP.hobbies.all)", a single scan may return tuples
 *		of many types, so now we return pointers to tuple descriptors
 *		along with tuples returned via the tuple table.  This means
 *		we now have a bunch of routines to diddle the slot descriptors
 *		too.  -cim 1/18/90
 *
 *		The tuple table stuff depends on the executor/tuptable.h macros,
 *		and the TupleTableSlot node in execnodes.h.
 *
 */

#include "postgres.h"
#include "executor/executor.h"

#undef ExecStoreTuple

#include "catalog/pg_type.h"
#include "access/heapam.h"

static TupleTableSlot *NodeGetResultTupleSlot(Plan *node);


/* ----------------------------------------------------------------
 *				  tuple table create/delete functions
 * ----------------------------------------------------------------
 */
/* --------------------------------
 *		ExecCreateTupleTable
 *
 *		This creates a new tuple table of the specified initial
 *		size.  If the size is insufficient, ExecAllocTableSlot()
 *		will grow the table as necessary.
 *
 *		This should be used by InitPlan() to allocate the table.
 *		The table's address will be stored in the EState structure.
 * --------------------------------
 */
TupleTable						/* return: address of table */
ExecCreateTupleTable(int initialSize)	/* initial number of slots in
										 * table */
{
	TupleTable	newtable;		/* newly allocated table */
	TupleTableSlot *array;		/* newly allocated slot array */

	/* ----------------
	 *	sanity checks
	 * ----------------
	 */
	Assert(initialSize >= 1);

	/* ----------------
	 *	Now allocate our new table along with space for the pointers
	 *	to the tuples.
	 */

	newtable = (TupleTable) palloc(sizeof(TupleTableData));
	array = (TupleTableSlot *) palloc(initialSize * sizeof(TupleTableSlot));

	/* ----------------
	 *	clean out the slots we just allocated
	 * ----------------
	 */
	MemSet(array, 0, initialSize * sizeof(TupleTableSlot));

	/* ----------------
	 *	initialize the new table and return it to the caller.
	 * ----------------
	 */
	newtable->size = initialSize;
	newtable->next = 0;
	newtable->array = array;

	return newtable;
}

/* --------------------------------
 *		ExecDropTupleTable
 *
 *		This pfrees the storage assigned to the tuple table and
 *		optionally pfrees the contents of the table also.
 *		It is expected that this routine be called by EndPlan().
 * --------------------------------
 */
void
ExecDropTupleTable(TupleTable table,	/* tuple table */
				   bool shouldFree)		/* true if we should free slot
										 * contents */
{
	int			next;			/* next available slot */
	TupleTableSlot *array;		/* start of table array */
	int			i;				/* counter */

	/* ----------------
	 *	sanity checks
	 * ----------------
	 */
	Assert(table != NULL);

	/* ----------------
	 *	get information from the table
	 * ----------------
	 */
	array = table->array;
	next = table->next;

	/* ----------------
	 *	first free all the valid pointers in the tuple array
	 *	and drop refcounts of any referenced buffers,
	 *	if that's what the caller wants.  (There is probably
	 *	no good reason for the caller ever not to want it!)
	 *
	 *	Note: we do nothing about the Tuple Descriptor's
	 *	we store in the slots.	This may have to change (ex: we should
	 *	probably worry about pfreeing tuple descs too) -cim 3/14/91
	 *
	 *	Right now, the handling of tuple pointers and buffer refcounts
	 *	is clean, but the handling of tuple descriptors is NOT; they
	 *	are copied around with wild abandon.  It would take some work
	 *	to make tuple descs pfree'able.  Fortunately, since they're
	 *	normally only made once per scan, it's probably not worth
	 *	worrying about...  tgl 9/21/99
	 * ----------------
	 */
	if (shouldFree)
	{
		for (i = 0; i < next; i++)
			ExecClearTuple(&array[i]);
	}

	/* ----------------
	 *	finally free the tuple array and the table itself.
	 * ----------------
	 */
	pfree(array);
	pfree(table);

}


/* ----------------------------------------------------------------
 *				  tuple table slot reservation functions
 * ----------------------------------------------------------------
 */
/* --------------------------------
 *		ExecAllocTableSlot
 *
 *		This routine is used to reserve slots in the table for
 *		use by the various plan nodes.	It is expected to be
 *		called by the node init routines (ex: ExecInitNestLoop).
 *		once per slot needed by the node.  Not all nodes need
 *		slots (some just pass tuples around).
 * --------------------------------
 */
TupleTableSlot *				/* return: the slot allocated in the tuple
								 * table */
ExecAllocTableSlot(TupleTable table)
{
	int			slotnum;		/* new slot number */
	TupleTableSlot *slot;

	/* ----------------
	 *	sanity checks
	 * ----------------
	 */
	Assert(table != NULL);

	/* ----------------
	 *	if our table is full we have to allocate a larger
	 *	size table.  Since ExecAllocTableSlot() is only called
	 *	before the table is ever used to store tuples, we don't
	 *	have to worry about the contents of the old table.
	 *	If this changes, then we will have to preserve the contents.
	 *	-cim 6/23/90
	 *
	 *	Unfortunately, we *cannot* do this.  All of the nodes in
	 *	the plan that have already initialized their slots will have
	 *	pointers into _freed_ memory.  This leads to bad ends.	We
	 *	now count the number of slots we will need and create all the
	 *	slots we will need ahead of time.  The if below should never
	 *	happen now.  Give a WARN if it does.  -mer 4 Aug 1992
	 * ----------------
	 */
	if (table->next >= table->size)
	{

		/*
		 * int newsize = NewTableSize(table->size);
		 *
		 * pfree(table->array); table->array = (Pointer) palloc(newsize *
		 * TableSlotSize); bzero(table->array, newsize * TableSlotSize);
		 * table->size =  newsize;
		 */
		elog(NOTICE, "Plan requires more slots than are available");
		elog(ERROR, "send mail to your local executor guru to fix this");
	}

	/* ----------------
	 *	at this point, space in the table is guaranteed so we
	 *	reserve the next slot, initialize and return it.
	 * ----------------
	 */
	slotnum = table->next;
	table->next++;

	slot = &(table->array[slotnum]);

	/* Make sure the allocated slot is valid (and empty) */
	slot->type = T_TupleTableSlot;
	slot->val = (HeapTuple) NULL;
	slot->ttc_shouldFree = true;
	slot->ttc_descIsNew = true;
	slot->ttc_tupleDescriptor = (TupleDesc) NULL;
	slot->ttc_buffer = InvalidBuffer;
	slot->ttc_whichplan = -1;

	return slot;
}

/* ----------------------------------------------------------------
 *				  tuple table slot accessor functions
 * ----------------------------------------------------------------
 */

/* --------------------------------
 *		ExecStoreTuple
 *
 *		This function is used to store a tuple into a specified
 *		slot in the tuple table.
 *
 *		tuple:	tuple to store
 *		slot:	slot to store it in
 *		buffer: disk buffer if tuple is in a disk page, else InvalidBuffer
 *		shouldFree: true if ExecClearTuple should pfree() the tuple
 *					when done with it
 *
 * If 'buffer' is not InvalidBuffer, the tuple table code acquires a pin
 * on the buffer which is held until the slot is cleared, so that the tuple
 * won't go away on us.
 *
 * shouldFree is normally set 'true' for tuples constructed on-the-fly.
 * It must always be 'false' for tuples that are stored in disk pages,
 * since we don't want to try to pfree those.
 *
 * Another case where it is 'false' is when the referenced tuple is held
 * in a tuple table slot belonging to a lower-level executor Proc node.
 * In this case the lower-level slot retains ownership and responsibility
 * for eventually releasing the tuple.	When this method is used, we must
 * be certain that the upper-level Proc node will lose interest in the tuple
 * sooner than the lower-level one does!  If you're not certain, copy the
 * lower-level tuple with heap_copytuple and let the upper-level table
 * slot assume ownership of the copy!
 *
 * Return value is just the passed-in slot pointer.
 * --------------------------------
 */
TupleTableSlot *
ExecStoreTuple(HeapTuple tuple,
			   TupleTableSlot *slot,
			   Buffer buffer,
			   bool shouldFree)
{
	/* ----------------
	 *	sanity checks
	 * ----------------
	 */
	Assert(slot != NULL);
	/* passing shouldFree=true for a tuple on a disk page is not sane */
	Assert(BufferIsValid(buffer) ? (!shouldFree) : true);

	/* clear out any old contents of the slot */
	ExecClearTuple(slot);

	/* ----------------
	 *	store the new tuple into the specified slot and
	 *	return the slot into which we stored the tuple.
	 * ----------------
	 */
	slot->val = tuple;
	slot->ttc_buffer = buffer;
	slot->ttc_shouldFree = shouldFree;

	/*
	 * If tuple is on a disk page, keep the page pinned as long as we hold
	 * a pointer into it.
	 */
	if (BufferIsValid(buffer))
		IncrBufferRefCount(buffer);

	return slot;
}

/* --------------------------------
 *		ExecClearTuple
 *
 *		This function is used to clear out a slot in the tuple table.
 * --------------------------------
 */
TupleTableSlot *				/* return: slot passed */
ExecClearTuple(TupleTableSlot *slot)	/* slot in which to store tuple */
{
	HeapTuple	oldtuple;		/* prior contents of slot */

	/* ----------------
	 *	sanity checks
	 * ----------------
	 */
	Assert(slot != NULL);

	/* ----------------
	 *	get information from the tuple table
	 * ----------------
	 */
	oldtuple = slot->val;

	/* ----------------
	 *	free the old contents of the specified slot if necessary.
	 * ----------------
	 */
	if (slot->ttc_shouldFree && oldtuple != NULL)
		heap_freetuple(oldtuple);

	slot->val = (HeapTuple) NULL;

	slot->ttc_shouldFree = true;/* probably useless code... */

	/* ----------------
	 *	Drop the pin on the referenced buffer, if there is one.
	 * ----------------
	 */
	if (BufferIsValid(slot->ttc_buffer))
		ReleaseBuffer(slot->ttc_buffer);

	slot->ttc_buffer = InvalidBuffer;

	return slot;
}


/* --------------------------------
 *		ExecSlotPolicy
 *
 *		This function is used to get the call/don't call pfree
 *		setting of a slot.	Most executor routines don't need this.
 *		It's only when you do tricky things like marking tuples for
 *		merge joins that you need to diddle the slot policy.
 * --------------------------------
 */
#ifdef NOT_USED
bool							/* return: slot policy */
ExecSlotPolicy(TupleTableSlot *slot)	/* slot to inspect */
{
	return slot->ttc_shouldFree;
}


/* --------------------------------
 *		ExecSetSlotPolicy
 *
 *		This function is used to change the call/don't call pfree
 *		setting of a slot.	Most executor routines don't need this.
 *		It's only when you do tricky things like marking tuples for
 *		merge joins that you need to diddle the slot policy.
 * --------------------------------
 */
bool							/* return: old slot policy */
ExecSetSlotPolicy(TupleTableSlot *slot, /* slot to change */
				  bool shouldFree)		/* true if we call pfree() when we
										 * gc. */
{
	bool		old_shouldFree = slot->ttc_shouldFree;

	slot->ttc_shouldFree = shouldFree;

	return old_shouldFree;
}

#endif

/* --------------------------------
 *		ExecSlotDescriptor
 *
 *		This function is used to get the tuple descriptor associated
 *		with the slot's tuple.
 *
 * Now a macro in tuptable.h  -mer 5 March 1992
 * --------------------------------
 */

/* --------------------------------
 *		ExecSetSlotDescriptor
 *
 *		This function is used to set the tuple descriptor associated
 *		with the slot's tuple.
 * --------------------------------
 */
TupleDesc						/* return: old slot tuple descriptor */
ExecSetSlotDescriptor(TupleTableSlot *slot,		/* slot to change */
					  TupleDesc tupdesc)		/* tuple descriptor */
{
	TupleDesc	old_tupdesc = slot->ttc_tupleDescriptor;

	slot->ttc_tupleDescriptor = tupdesc;
	return old_tupdesc;
}

/* --------------------------------
 *		ExecSetSlotDescriptorIsNew
 *
 *		This function is used to change the setting of the "isNew" flag
 * --------------------------------
 */
void
ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,		/* slot to change */
						   bool isNew)	/* "isNew" setting */
{
	slot->ttc_descIsNew = isNew;
}

/* --------------------------------
 *		ExecSetNewSlotDescriptor
 *
 *		This function is used to set the tuple descriptor associated
 *		with the slot's tuple, and set the "isNew" flag at the same time.
 * --------------------------------
 */
#ifdef NOT_USED
TupleDesc						/* return: old slot tuple descriptor */
ExecSetNewSlotDescriptor(TupleTableSlot *slot,	/* slot to change */
						 TupleDesc tupdesc)		/* tuple descriptor */
{
	TupleDesc	old_tupdesc = slot->ttc_tupleDescriptor;

	slot->ttc_tupleDescriptor = tupdesc;
	slot->ttc_descIsNew = true;

	return old_tupdesc;
}

#endif

/* ----------------------------------------------------------------
 *				  tuple table slot status predicates
 * ----------------------------------------------------------------
 */

/* --------------------------------
 *		ExecSlotDescriptorIsNew
 *
 *		This function is used to check if the tuple descriptor
 *		associated with this slot has just changed.  ie: we are
 *		now storing a new type of tuple in this slot
 * --------------------------------
 */
#ifdef NOT_USED
bool							/* return: descriptor "is new" */
ExecSlotDescriptorIsNew(TupleTableSlot *slot)	/* slot to inspect */
{
/*	  bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot);
	return isNew; */
	return slot->ttc_descIsNew;
}

#endif

/* ----------------------------------------------------------------
 *				convenience initialization routines
 * ----------------------------------------------------------------
 */
/* --------------------------------
 *		ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot
 *
 *		These are convenience routines to initialize the specfied slot
 *		in nodes inheriting the appropriate state.
 * --------------------------------
 */
#define INIT_SLOT_DEFS \
	TupleTable	   tupleTable; \
	TupleTableSlot*   slot

#define INIT_SLOT_ALLOC \
	tupleTable = (TupleTable) estate->es_tupleTable; \
	slot =		 ExecAllocTableSlot(tupleTable);

/* ----------------
 *		ExecInitResultTupleSlot
 * ----------------
 */
void
ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
{
	INIT_SLOT_DEFS;
	INIT_SLOT_ALLOC;
	commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
}

/* ----------------
 *		ExecInitScanTupleSlot
 * ----------------
 */
void
ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
{
	INIT_SLOT_DEFS;
	INIT_SLOT_ALLOC;
	commonscanstate->css_ScanTupleSlot = (TupleTableSlot *) slot;
}

#ifdef NOT_USED
/* ----------------
 *		ExecInitMarkedTupleSlot
 * ----------------
 */
void
ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate)
{
	INIT_SLOT_DEFS;
	INIT_SLOT_ALLOC;
	mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
}

#endif

/* ----------------
 *		ExecInitOuterTupleSlot
 * ----------------
 */
void
ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate)
{
	INIT_SLOT_DEFS;
	INIT_SLOT_ALLOC;
	hashstate->hj_OuterTupleSlot = slot;
}

/* ----------------
 *		ExecInitHashTupleSlot
 * ----------------
 */
#ifdef NOT_USED
void
ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
{
	INIT_SLOT_DEFS;
	INIT_SLOT_ALLOC;
	hashstate->hj_HashTupleSlot = slot;
}

#endif

static TupleTableSlot *
NodeGetResultTupleSlot(Plan *node)
{
	TupleTableSlot *slot;

	switch (nodeTag(node))
	{

		case T_Result:
			{
				ResultState *resstate = ((Result *) node)->resstate;

				slot = resstate->cstate.cs_ResultTupleSlot;
			}
			break;

		case T_SeqScan:
			{
				CommonScanState *scanstate = ((SeqScan *) node)->scanstate;

				slot = scanstate->cstate.cs_ResultTupleSlot;
			}
			break;

		case T_NestLoop:
			{
				NestLoopState *nlstate = ((NestLoop *) node)->nlstate;

				slot = nlstate->jstate.cs_ResultTupleSlot;
			}
			break;

		case T_Append:
			{
				Append	   *n = (Append *) node;
				AppendState *appendstate;
				List	   *appendplans;
				int			whichplan;
				Plan	   *subplan;

				appendstate = n->appendstate;
				appendplans = n->appendplans;
				whichplan = appendstate->as_whichplan;

				subplan = (Plan *) nth(whichplan, appendplans);
				slot = NodeGetResultTupleSlot(subplan);
				break;
			}

		case T_IndexScan:
			{
				CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate;

				slot = scanstate->cstate.cs_ResultTupleSlot;
			}
			break;

		case T_Material:
			{
				MaterialState *matstate = ((Material *) node)->matstate;

				slot = matstate->csstate.css_ScanTupleSlot;
			}
			break;

		case T_Sort:
			{
				SortState  *sortstate = ((Sort *) node)->sortstate;

				slot = sortstate->csstate.css_ScanTupleSlot;
			}
			break;

		case T_Agg:
			{
				AggState   *aggstate = ((Agg *) node)->aggstate;

				slot = aggstate->csstate.cstate.cs_ResultTupleSlot;
			}
			break;

		case T_Group:
			{
				GroupState *grpstate = ((Group *) node)->grpstate;

				slot = grpstate->csstate.cstate.cs_ResultTupleSlot;
			}
			break;

		case T_Hash:
			{
				HashState  *hashstate = ((Hash *) node)->hashstate;

				slot = hashstate->cstate.cs_ResultTupleSlot;
			}
			break;

		case T_Unique:
			{
				UniqueState *uniquestate = ((Unique *) node)->uniquestate;

				slot = uniquestate->cstate.cs_ResultTupleSlot;
			}
			break;

		case T_MergeJoin:
			{
				MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate;

				slot = mergestate->jstate.cs_ResultTupleSlot;
			}
			break;

		case T_HashJoin:
			{
				HashJoinState *hashjoinstate = ((HashJoin *) node)->hashjoinstate;

				slot = hashjoinstate->jstate.cs_ResultTupleSlot;
			}
			break;

		case T_TidScan:
			{
				CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate;

				slot = scanstate->cstate.cs_ResultTupleSlot;
			}
			break;

		default:
			/* ----------------
			 *	  should never get here
			 * ----------------
			 */
			elog(ERROR, "NodeGetResultTupleSlot: node not yet supported: %d ",
				 nodeTag(node));

			return NULL;
	}
	return slot;
}

/* ----------------------------------------------------------------
 *		ExecGetTupType
 *
 *		this gives you the tuple descriptor for tuples returned
 *		by this node.  I really wish I could ditch this routine,
 *		but since not all nodes store their type info in the same
 *		place, we have to do something special for each node type.
 *
 *		Soon, the system will have to adapt to deal with changing
 *		tuple descriptors as we deal with dynamic tuple types
 *		being returned from procedure nodes.  Perhaps then this
 *		routine can be retired.  -cim 6/3/91
 *
 * old comments
 *		This routine just gets the type information out of the
 *		node's state.  If you already have a node's state, you
 *		can get this information directly, but this is a useful
 *		routine if you want to get the type information from
 *		the node's inner or outer subplan easily without having
 *		to inspect the subplan.. -cim 10/16/89
 *
 * ----------------------------------------------------------------
 */

TupleDesc
ExecGetTupType(Plan *node)
{
	TupleTableSlot *slot;
	TupleDesc	tupType;

	if (node == NULL)
		return NULL;

	slot = NodeGetResultTupleSlot(node);
	tupType = slot->ttc_tupleDescriptor;
	return tupType;
}

/*
TupleDesc
ExecCopyTupType(TupleDesc td, int natts)
{
	TupleDesc newTd;
	int				i;

	newTd = CreateTemplateTupleDesc(natts);
	i = 0;
	while (i < natts)
		{
			newTd[i] = (Form_pg_attribute)palloc(sizeof(FormData_pg_attribute));
			memmove(newTd[i], td[i], sizeof(FormData_pg_attribute));
			i++;
		}
	return newTd;
}
*/

/* ----------------------------------------------------------------
 *		ExecTypeFromTL
 *
 *		Currently there are about 4 different places where we create
 *		TupleDescriptors.  They should all be merged, or perhaps
 *		be rewritten to call BuildDesc().
 *
 *	old comments
 *		Forms attribute type info from the target list in the node.
 *		It assumes all domains are individually specified in the target list.
 *		It fails if the target list contains something like Emp.all
 *		which represents all the attributes from EMP relation.
 *
 *		Conditions:
 *			The inner and outer subtrees should be initialized because it
 *			might be necessary to know the type infos of the subtrees.
 * ----------------------------------------------------------------
 */
TupleDesc
ExecTypeFromTL(List *targetList)
{
	List	   *tlcdr;
	TupleDesc	typeInfo;
	Resdom	   *resdom;
	Oid			restype;
	int			len;

	/* ----------------
	 *	examine targetlist - if empty then return NULL
	 * ----------------
	 */
	len = ExecTargetListLength(targetList);

	if (len == 0)
		return NULL;

	/* ----------------
	 *	allocate a new typeInfo
	 * ----------------
	 */
	typeInfo = CreateTemplateTupleDesc(len);

	/* ----------------
	 * notes: get resdom from (resdom expr)
	 *		  get_typbyval comes from src/lib/l-lisp/lsyscache.c
	 * ----------------
	 */
	tlcdr = targetList;
	while (tlcdr != NIL)
	{
		TargetEntry *tle = lfirst(tlcdr);

		if (tle->resdom != NULL)
		{
			resdom = tle->resdom;
			restype = resdom->restype;

			TupleDescInitEntry(typeInfo,
							   resdom->resno,
							   resdom->resname,
			/* fix for SELECT NULL ... */
							   (restype ? restype : UNKNOWNOID),
							   resdom->restypmod,
							   0,
							   false);

/*
			ExecSetTypeInfo(resdom->resno - 1,
							typeInfo,
							(Oid) restype,
							resdom->resno,
							resdom->reslen,
							NameStr(*resdom->resname),
							get_typbyval(restype),
							get_typalign(restype));
*/
		}
		else
		{
			Resdom	   *fjRes;
			List	   *fjTlistP;
			List	   *fjList = lfirst(tlcdr);

#ifdef SETS_FIXED
			TargetEntry *tle;
			Fjoin	   *fjNode = ((TargetEntry *) lfirst(fjList))->fjoin;

			tle = fjNode->fj_innerNode; /* ??? */
#endif
			fjRes = tle->resdom;
			restype = fjRes->restype;

			TupleDescInitEntry(typeInfo,
							   fjRes->resno,
							   fjRes->resname,
							   restype,
							   fjRes->restypmod,
							   0,
							   false);
/*
			ExecSetTypeInfo(fjRes->resno - 1,
							typeInfo,
							(Oid) restype,
							fjRes->resno,
							fjRes->reslen,
							(char *) fjRes->resname,
							get_typbyval(restype),
							get_typalign(restype));
*/

			foreach(fjTlistP, lnext(fjList))
			{
				TargetEntry *fjTle = lfirst(fjTlistP);

				fjRes = fjTle->resdom;

				TupleDescInitEntry(typeInfo,
								   fjRes->resno,
								   fjRes->resname,
								   restype,
								   fjRes->restypmod,
								   0,
								   false);

/*
				ExecSetTypeInfo(fjRes->resno - 1,
								typeInfo,
								(Oid) fjRes->restype,
								fjRes->resno,
								fjRes->reslen,
								(char *) fjRes->resname,
								get_typbyval(fjRes->restype),
								get_typalign(fjRes->restype));
*/
			}
		}

		tlcdr = lnext(tlcdr);
	}

	return typeInfo;
}
