postgresql/src/backend/executor/execTuples.c
Tom Lane e812458b27 Several changes here, not very related but touching some of the same files.
* Buffer refcount cleanup (per my "progress report" to pghackers, 9/22).
* Add links to backend PROC structs to sinval's array of per-backend info,
and use these links for routines that need to check the state of all
backends (rather than the slow, complicated search of the ShmemIndex
hashtable that was used before).  Add databaseOID to PROC structs.
* Use this to implement an interlock that prevents DESTROY DATABASE of
a database containing running backends.  (It's a little tricky to prevent
a concurrently-starting backend from getting in there, since the new
backend is not able to lock anything at the time it tries to look up
its database in pg_database.  My solution is to recheck that the DB is
OK at the end of InitPostgres.  It may not be a 100% solution, but it's
a lot better than no interlock at all...)
* In ALTER TABLE RENAME, flush buffers for the relation before doing the
rename of the physical files, to ensure we don't get failures later from
mdblindwrt().
* Update TRUNCATE patch so that it actually compiles against current
sources :-(.
You should do "make clean all" after pulling these changes.
1999-09-24 00:25:33 +00:00

986 lines
26 KiB
C

/*-------------------------------------------------------------------------
*
* 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.).
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.30 1999/09/24 00:24:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
*
* TABLE CREATE/DELETE
* ExecCreateTupleTable - create a new tuple table
* ExecDestroyTupleTable - 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 ExecDestroyTupleTable() 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"
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;
}
/* --------------------------------
* ExecDestroyTupleTable
*
* 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
ExecDestroyTupleTable(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)
pfree(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->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;
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,
resdom->resname->data,
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;
}