/*------------------------------------------------------------------------- * * execTuples.c * Routines dealing with TupleTableSlots. These are used for resource * management associated with tuples (eg, releasing buffer pins for * tuples in disk buffers, or freeing the memory occupied by transient * tuples). Slots also provide access abstraction that lets us implement * "virtual" tuples to reduce data-copying overhead. * * 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-2012, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/executor/execTuples.c * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES * * SLOT CREATION/DESTRUCTION * MakeTupleTableSlot - create an empty slot * ExecAllocTableSlot - create a slot within a tuple table * ExecResetTupleTable - clear and optionally delete a tuple table * MakeSingleTupleTableSlot - make a standalone slot, set its descriptor * ExecDropSingleTupleTableSlot - destroy a standalone slot * * SLOT ACCESSORS * ExecSetSlotDescriptor - set a slot's tuple descriptor * ExecStoreTuple - store a physical tuple in the slot * ExecStoreMinimalTuple - store a minimal physical tuple in the slot * ExecClearTuple - clear contents of a slot * ExecStoreVirtualTuple - mark slot as containing a virtual tuple * ExecCopySlotTuple - build a physical tuple from a slot * ExecCopySlotMinimalTuple - build a minimal physical tuple from a slot * ExecMaterializeSlot - convert virtual to physical storage * ExecCopySlot - copy one slot's contents to another * * CONVENIENCE INITIALIZATION ROUTINES * ExecInitResultTupleSlot \ convenience routines to initialize * ExecInitScanTupleSlot \ the various tuple slots for nodes * ExecInitExtraTupleSlot / which store copies of tuples. * ExecInitNullTupleSlot / * * Routines that probably belong somewhere else: * ExecTypeFromTL - form a TupleDesc from a target list * * EXAMPLE OF HOW TABLE ROUTINES WORK * Suppose we have a query such as SELECT emp.name FROM emp and we have * a single SeqScan node in the query plan. * * At ExecutorStart() * ---------------- * - ExecInitSeqScan() calls ExecInitScanTupleSlot() and * ExecInitResultTupleSlot() to construct TupleTableSlots * for the tuples returned by the access methods and the * tuples resulting from performing target list projections. * * During ExecutorRun() * ---------------- * - 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 ExecProject() and place it into the result tuple slot. * * - ExecutePlan() calls ExecSelect(), which passes the result slot * to printtup(), which uses slot_getallattrs() to extract the * individual Datums for printing. * * At ExecutorEnd() * ---------------- * - EndPlan() calls ExecResetTupleTable() 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). It also allows us * to avoid physically constructing projection tuples in many cases. */ #include "postgres.h" #include "funcapi.h" #include "catalog/pg_type.h" #include "nodes/nodeFuncs.h" #include "storage/bufmgr.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/typcache.h" static TupleDesc ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk); /* ---------------------------------------------------------------- * tuple table create/delete functions * ---------------------------------------------------------------- */ /* -------------------------------- * MakeTupleTableSlot * * Basic routine to make an empty TupleTableSlot. * -------------------------------- */ TupleTableSlot * MakeTupleTableSlot(void) { TupleTableSlot *slot = makeNode(TupleTableSlot); slot->tts_isempty = true; slot->tts_shouldFree = false; slot->tts_shouldFreeMin = false; slot->tts_tuple = NULL; slot->tts_tupleDescriptor = NULL; slot->tts_mcxt = CurrentMemoryContext; slot->tts_buffer = InvalidBuffer; slot->tts_nvalid = 0; slot->tts_values = NULL; slot->tts_isnull = NULL; slot->tts_mintuple = NULL; return slot; } /* -------------------------------- * ExecAllocTableSlot * * Create a tuple table slot within a tuple table (which is just a List). * -------------------------------- */ TupleTableSlot * ExecAllocTableSlot(List **tupleTable) { TupleTableSlot *slot = MakeTupleTableSlot(); *tupleTable = lappend(*tupleTable, slot); return slot; } /* -------------------------------- * ExecResetTupleTable * * This releases any resources (buffer pins, tupdesc refcounts) * held by the tuple table, and optionally releases the memory * occupied by the tuple table data structure. * It is expected that this routine be called by EndPlan(). * -------------------------------- */ void ExecResetTupleTable(List *tupleTable, /* tuple table */ bool shouldFree) /* true if we should free memory */ { ListCell *lc; foreach(lc, tupleTable) { TupleTableSlot *slot = (TupleTableSlot *) lfirst(lc); /* Sanity checks */ Assert(IsA(slot, TupleTableSlot)); /* Always release resources and reset the slot to empty */ ExecClearTuple(slot); if (slot->tts_tupleDescriptor) { ReleaseTupleDesc(slot->tts_tupleDescriptor); slot->tts_tupleDescriptor = NULL; } /* If shouldFree, release memory occupied by the slot itself */ if (shouldFree) { if (slot->tts_values) pfree(slot->tts_values); if (slot->tts_isnull) pfree(slot->tts_isnull); pfree(slot); } } /* If shouldFree, release the list structure */ if (shouldFree) list_free(tupleTable); } /* -------------------------------- * MakeSingleTupleTableSlot * * This is a convenience routine for operations that need a * standalone TupleTableSlot not gotten from the main executor * tuple table. It makes a single slot and initializes it * to use the given tuple descriptor. * -------------------------------- */ TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc) { TupleTableSlot *slot = MakeTupleTableSlot(); ExecSetSlotDescriptor(slot, tupdesc); return slot; } /* -------------------------------- * ExecDropSingleTupleTableSlot * * Release a TupleTableSlot made with MakeSingleTupleTableSlot. * DON'T use this on a slot that's part of a tuple table list! * -------------------------------- */ void ExecDropSingleTupleTableSlot(TupleTableSlot *slot) { /* This should match ExecResetTupleTable's processing of one slot */ Assert(IsA(slot, TupleTableSlot)); ExecClearTuple(slot); if (slot->tts_tupleDescriptor) ReleaseTupleDesc(slot->tts_tupleDescriptor); if (slot->tts_values) pfree(slot->tts_values); if (slot->tts_isnull) pfree(slot->tts_isnull); pfree(slot); } /* ---------------------------------------------------------------- * tuple table slot accessor functions * ---------------------------------------------------------------- */ /* -------------------------------- * ExecSetSlotDescriptor * * This function is used to set the tuple descriptor associated * with the slot's tuple. The passed descriptor must have lifespan * at least equal to the slot's. If it is a reference-counted descriptor * then the reference count is incremented for as long as the slot holds * a reference. * -------------------------------- */ void ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ TupleDesc tupdesc) /* new tuple descriptor */ { /* For safety, make sure slot is empty before changing it */ ExecClearTuple(slot); /* * Release any old descriptor. Also release old Datum/isnull arrays if * present (we don't bother to check if they could be re-used). */ if (slot->tts_tupleDescriptor) ReleaseTupleDesc(slot->tts_tupleDescriptor); if (slot->tts_values) pfree(slot->tts_values); if (slot->tts_isnull) pfree(slot->tts_isnull); /* * Install the new descriptor; if it's refcounted, bump its refcount. */ slot->tts_tupleDescriptor = tupdesc; PinTupleDesc(tupdesc); /* * Allocate Datum/isnull arrays of the appropriate size. These must have * the same lifetime as the slot, so allocate in the slot's own context. */ slot->tts_values = (Datum *) MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(Datum)); slot->tts_isnull = (bool *) MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(bool)); } /* -------------------------------- * ExecStoreTuple * * This function is used to store a physical 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. * * NOTE: before PostgreSQL 8.1, this function would accept a NULL tuple * pointer and effectively behave like ExecClearTuple (though you could * still specify a buffer to pin, which would be an odd combination). * This saved a couple lines of code in a few places, but seemed more likely * to mask logic errors than to be really useful, so it's now disallowed. * -------------------------------- */ TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree) { /* * sanity checks */ Assert(tuple != NULL); Assert(slot != NULL); Assert(slot->tts_tupleDescriptor != NULL); /* passing shouldFree=true for a tuple on a disk page is not sane */ Assert(BufferIsValid(buffer) ? (!shouldFree) : true); /* * Free any old physical tuple belonging to the slot. */ if (slot->tts_shouldFree) heap_freetuple(slot->tts_tuple); if (slot->tts_shouldFreeMin) heap_free_minimal_tuple(slot->tts_mintuple); /* * Store the new tuple into the specified slot. */ slot->tts_isempty = false; slot->tts_shouldFree = shouldFree; slot->tts_shouldFreeMin = false; slot->tts_tuple = tuple; slot->tts_mintuple = NULL; /* Mark extracted state invalid */ slot->tts_nvalid = 0; /* * If tuple is on a disk page, keep the page pinned as long as we hold a * pointer into it. We assume the caller already has such a pin. * * This is coded to optimize the case where the slot previously held a * tuple on the same disk page: in that case releasing and re-acquiring * the pin is a waste of cycles. This is a common situation during * seqscans, so it's worth troubling over. */ if (slot->tts_buffer != buffer) { if (BufferIsValid(slot->tts_buffer)) ReleaseBuffer(slot->tts_buffer); slot->tts_buffer = buffer; if (BufferIsValid(buffer)) IncrBufferRefCount(buffer); } return slot; } /* -------------------------------- * ExecStoreMinimalTuple * * Like ExecStoreTuple, but insert a "minimal" tuple into the slot. * * No 'buffer' parameter since minimal tuples are never stored in relations. * -------------------------------- */ TupleTableSlot * ExecStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot, bool shouldFree) { /* * sanity checks */ Assert(mtup != NULL); Assert(slot != NULL); Assert(slot->tts_tupleDescriptor != NULL); /* * Free any old physical tuple belonging to the slot. */ if (slot->tts_shouldFree) heap_freetuple(slot->tts_tuple); if (slot->tts_shouldFreeMin) heap_free_minimal_tuple(slot->tts_mintuple); /* * Drop the pin on the referenced buffer, if there is one. */ if (BufferIsValid(slot->tts_buffer)) ReleaseBuffer(slot->tts_buffer); slot->tts_buffer = InvalidBuffer; /* * Store the new tuple into the specified slot. */ slot->tts_isempty = false; slot->tts_shouldFree = false; slot->tts_shouldFreeMin = shouldFree; slot->tts_tuple = &slot->tts_minhdr; slot->tts_mintuple = mtup; slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET; slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET); /* no need to set t_self or t_tableOid since we won't allow access */ /* Mark extracted state invalid */ slot->tts_nvalid = 0; return slot; } /* -------------------------------- * ExecClearTuple * * This function is used to clear out a slot in the tuple table. * * NB: only the tuple is cleared, not the tuple descriptor (if any). * -------------------------------- */ TupleTableSlot * /* return: slot passed */ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */ { /* * sanity checks */ Assert(slot != NULL); /* * Free the old physical tuple if necessary. */ if (slot->tts_shouldFree) heap_freetuple(slot->tts_tuple); if (slot->tts_shouldFreeMin) heap_free_minimal_tuple(slot->tts_mintuple); slot->tts_tuple = NULL; slot->tts_mintuple = NULL; slot->tts_shouldFree = false; slot->tts_shouldFreeMin = false; /* * Drop the pin on the referenced buffer, if there is one. */ if (BufferIsValid(slot->tts_buffer)) ReleaseBuffer(slot->tts_buffer); slot->tts_buffer = InvalidBuffer; /* * Mark it empty. */ slot->tts_isempty = true; slot->tts_nvalid = 0; return slot; } /* -------------------------------- * ExecStoreVirtualTuple * Mark a slot as containing a virtual tuple. * * The protocol for loading a slot with virtual tuple data is: * * Call ExecClearTuple to mark the slot empty. * * Store data into the Datum/isnull arrays. * * Call ExecStoreVirtualTuple to mark the slot valid. * This is a bit unclean but it avoids one round of data copying. * -------------------------------- */ TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot) { /* * sanity checks */ Assert(slot != NULL); Assert(slot->tts_tupleDescriptor != NULL); Assert(slot->tts_isempty); slot->tts_isempty = false; slot->tts_nvalid = slot->tts_tupleDescriptor->natts; return slot; } /* -------------------------------- * ExecStoreAllNullTuple * Set up the slot to contain a null in every column. * * At first glance this might sound just like ExecClearTuple, but it's * entirely different: the slot ends up full, not empty. * -------------------------------- */ TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot) { /* * sanity checks */ Assert(slot != NULL); Assert(slot->tts_tupleDescriptor != NULL); /* Clear any old contents */ ExecClearTuple(slot); /* * Fill all the columns of the virtual tuple with nulls */ MemSet(slot->tts_values, 0, slot->tts_tupleDescriptor->natts * sizeof(Datum)); memset(slot->tts_isnull, true, slot->tts_tupleDescriptor->natts * sizeof(bool)); return ExecStoreVirtualTuple(slot); } /* -------------------------------- * ExecCopySlotTuple * Obtain a copy of a slot's regular physical tuple. The copy is * palloc'd in the current memory context. * The slot itself is undisturbed. * * This works even if the slot contains a virtual or minimal tuple; * however the "system columns" of the result will not be meaningful. * -------------------------------- */ HeapTuple ExecCopySlotTuple(TupleTableSlot *slot) { /* * sanity checks */ Assert(slot != NULL); Assert(!slot->tts_isempty); /* * If we have a physical tuple (either format) then just copy it. */ if (TTS_HAS_PHYSICAL_TUPLE(slot)) return heap_copytuple(slot->tts_tuple); if (slot->tts_mintuple) return heap_tuple_from_minimal_tuple(slot->tts_mintuple); /* * Otherwise we need to build a tuple from the Datum array. */ return heap_form_tuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull); } /* -------------------------------- * ExecCopySlotMinimalTuple * Obtain a copy of a slot's minimal physical tuple. The copy is * palloc'd in the current memory context. * The slot itself is undisturbed. * -------------------------------- */ MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot) { /* * sanity checks */ Assert(slot != NULL); Assert(!slot->tts_isempty); /* * If we have a physical tuple then just copy it. Prefer to copy * tts_mintuple since that's a tad cheaper. */ if (slot->tts_mintuple) return heap_copy_minimal_tuple(slot->tts_mintuple); if (slot->tts_tuple) return minimal_tuple_from_heap_tuple(slot->tts_tuple); /* * Otherwise we need to build a tuple from the Datum array. */ return heap_form_minimal_tuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull); } /* -------------------------------- * ExecFetchSlotTuple * Fetch the slot's regular physical tuple. * * If the slot contains a virtual tuple, we convert it to physical * form. The slot retains ownership of the physical tuple. * If it contains a minimal tuple we convert to regular form and store * that in addition to the minimal tuple (not instead of, because * callers may hold pointers to Datums within the minimal tuple). * * The main difference between this and ExecMaterializeSlot() is that this * does not guarantee that the contained tuple is local storage. * Hence, the result must be treated as read-only. * -------------------------------- */ HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot) { /* * sanity checks */ Assert(slot != NULL); Assert(!slot->tts_isempty); /* * If we have a regular physical tuple then just return it. */ if (TTS_HAS_PHYSICAL_TUPLE(slot)) return slot->tts_tuple; /* * Otherwise materialize the slot... */ return ExecMaterializeSlot(slot); } /* -------------------------------- * ExecFetchSlotMinimalTuple * Fetch the slot's minimal physical tuple. * * If the slot contains a virtual tuple, we convert it to minimal * physical form. The slot retains ownership of the minimal tuple. * If it contains a regular tuple we convert to minimal form and store * that in addition to the regular tuple (not instead of, because * callers may hold pointers to Datums within the regular tuple). * * As above, the result must be treated as read-only. * -------------------------------- */ MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot) { MemoryContext oldContext; /* * sanity checks */ Assert(slot != NULL); Assert(!slot->tts_isempty); /* * If we have a minimal physical tuple (local or not) then just return it. */ if (slot->tts_mintuple) return slot->tts_mintuple; /* * Otherwise, copy or build a minimal tuple, and store it into the slot. * * We may be called in a context that is shorter-lived than the tuple * slot, but we have to ensure that the materialized tuple will survive * anyway. */ oldContext = MemoryContextSwitchTo(slot->tts_mcxt); slot->tts_mintuple = ExecCopySlotMinimalTuple(slot); slot->tts_shouldFreeMin = true; MemoryContextSwitchTo(oldContext); /* * Note: we may now have a situation where we have a local minimal tuple * attached to a virtual or non-local physical tuple. There seems no harm * in that at the moment, but if any materializes, we should change this * function to force the slot into minimal-tuple-only state. */ return slot->tts_mintuple; } /* -------------------------------- * ExecFetchSlotTupleDatum * Fetch the slot's tuple as a composite-type Datum. * * We convert the slot's contents to local physical-tuple form, * and fill in the Datum header fields. Note that the result * always points to storage owned by the slot. * -------------------------------- */ Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot) { HeapTuple tup; HeapTupleHeader td; TupleDesc tupdesc; /* Make sure we can scribble on the slot contents ... */ tup = ExecMaterializeSlot(slot); /* ... and set up the composite-Datum header fields, in case not done */ td = tup->t_data; tupdesc = slot->tts_tupleDescriptor; HeapTupleHeaderSetDatumLength(td, tup->t_len); HeapTupleHeaderSetTypeId(td, tupdesc->tdtypeid); HeapTupleHeaderSetTypMod(td, tupdesc->tdtypmod); return PointerGetDatum(td); } /* -------------------------------- * ExecMaterializeSlot * Force a slot into the "materialized" state. * * This causes the slot's tuple to be a local copy not dependent on * any external storage. A pointer to the contained tuple is returned. * * A typical use for this operation is to prepare a computed tuple * for being stored on disk. The original data may or may not be * virtual, but in any case we need a private copy for heap_insert * to scribble on. * -------------------------------- */ HeapTuple ExecMaterializeSlot(TupleTableSlot *slot) { MemoryContext oldContext; /* * sanity checks */ Assert(slot != NULL); Assert(!slot->tts_isempty); /* * If we have a regular physical tuple, and it's locally palloc'd, we have * nothing to do. */ if (slot->tts_tuple && slot->tts_shouldFree) return slot->tts_tuple; /* * Otherwise, copy or build a physical tuple, and store it into the slot. * * We may be called in a context that is shorter-lived than the tuple * slot, but we have to ensure that the materialized tuple will survive * anyway. */ oldContext = MemoryContextSwitchTo(slot->tts_mcxt); slot->tts_tuple = ExecCopySlotTuple(slot); slot->tts_shouldFree = true; MemoryContextSwitchTo(oldContext); /* * Drop the pin on the referenced buffer, if there is one. */ if (BufferIsValid(slot->tts_buffer)) ReleaseBuffer(slot->tts_buffer); slot->tts_buffer = InvalidBuffer; /* * Mark extracted state invalid. This is important because the slot is * not supposed to depend any more on the previous external data; we * mustn't leave any dangling pass-by-reference datums in tts_values. * However, we have not actually invalidated any such datums, if there * happen to be any previously fetched from the slot. (Note in particular * that we have not pfree'd tts_mintuple, if there is one.) */ slot->tts_nvalid = 0; /* * On the same principle of not depending on previous remote storage, * forget the mintuple if it's not local storage. (If it is local * storage, we must not pfree it now, since callers might have already * fetched datum pointers referencing it.) */ if (!slot->tts_shouldFreeMin) slot->tts_mintuple = NULL; return slot->tts_tuple; } /* -------------------------------- * ExecCopySlot * Copy the source slot's contents into the destination slot. * * The destination acquires a private copy that will not go away * if the source is cleared. * * The caller must ensure the slots have compatible tupdescs. * -------------------------------- */ TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot) { HeapTuple newTuple; MemoryContext oldContext; /* * There might be ways to optimize this when the source is virtual, but * for now just always build a physical copy. Make sure it is in the * right context. */ oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt); newTuple = ExecCopySlotTuple(srcslot); MemoryContextSwitchTo(oldContext); return ExecStoreTuple(newTuple, dstslot, InvalidBuffer, true); } /* ---------------------------------------------------------------- * convenience initialization routines * ---------------------------------------------------------------- */ /* -------------------------------- * ExecInit{Result,Scan,Extra}TupleSlot * * These are convenience routines to initialize the specified slot * in nodes inheriting the appropriate state. ExecInitExtraTupleSlot * is used for initializing special-purpose slots. * -------------------------------- */ /* ---------------- * ExecInitResultTupleSlot * ---------------- */ void ExecInitResultTupleSlot(EState *estate, PlanState *planstate) { planstate->ps_ResultTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable); } /* ---------------- * ExecInitScanTupleSlot * ---------------- */ void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate) { scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable); } /* ---------------- * ExecInitExtraTupleSlot * ---------------- */ TupleTableSlot * ExecInitExtraTupleSlot(EState *estate) { return ExecAllocTableSlot(&estate->es_tupleTable); } /* ---------------- * ExecInitNullTupleSlot * * Build a slot containing an all-nulls tuple of the given type. * This is used as a substitute for an input tuple when performing an * outer join. * ---------------- */ TupleTableSlot * ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) { TupleTableSlot *slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupType); return ExecStoreAllNullTuple(slot); } /* ---------------------------------------------------------------- * ExecTypeFromTL * * Generate a tuple descriptor for the result tuple of a targetlist. * (A parse/plan tlist must be passed, not an ExprState tlist.) * Note that resjunk columns, if any, are included in the result. * * Currently there are about 4 different places where we create * TupleDescriptors. They should all be merged, or perhaps * be rewritten to call BuildDesc(). * ---------------------------------------------------------------- */ TupleDesc ExecTypeFromTL(List *targetList, bool hasoid) { return ExecTypeFromTLInternal(targetList, hasoid, false); } /* ---------------------------------------------------------------- * ExecCleanTypeFromTL * * Same as above, but resjunk columns are omitted from the result. * ---------------------------------------------------------------- */ TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid) { return ExecTypeFromTLInternal(targetList, hasoid, true); } static TupleDesc ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) { TupleDesc typeInfo; ListCell *l; int len; int cur_resno = 1; if (skipjunk) len = ExecCleanTargetListLength(targetList); else len = ExecTargetListLength(targetList); typeInfo = CreateTemplateTupleDesc(len, hasoid); foreach(l, targetList) { TargetEntry *tle = lfirst(l); if (skipjunk && tle->resjunk) continue; TupleDescInitEntry(typeInfo, cur_resno, tle->resname, exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr), 0); TupleDescInitEntryCollation(typeInfo, cur_resno, exprCollation((Node *) tle->expr)); cur_resno++; } return typeInfo; } /* * ExecTypeFromExprList - build a tuple descriptor from a list of Exprs * * Caller must also supply a list of field names (String nodes). */ TupleDesc ExecTypeFromExprList(List *exprList, List *namesList) { TupleDesc typeInfo; ListCell *le; ListCell *ln; int cur_resno = 1; Assert(list_length(exprList) == list_length(namesList)); typeInfo = CreateTemplateTupleDesc(list_length(exprList), false); forboth(le, exprList, ln, namesList) { Node *e = lfirst(le); char *n = strVal(lfirst(ln)); TupleDescInitEntry(typeInfo, cur_resno, n, exprType(e), exprTypmod(e), 0); TupleDescInitEntryCollation(typeInfo, cur_resno, exprCollation(e)); cur_resno++; } return typeInfo; } /* * BlessTupleDesc - make a completed tuple descriptor useful for SRFs * * Rowtype Datums returned by a function must contain valid type information. * This happens "for free" if the tupdesc came from a relcache entry, but * not if we have manufactured a tupdesc for a transient RECORD datatype. * In that case we have to notify typcache.c of the existence of the type. */ TupleDesc BlessTupleDesc(TupleDesc tupdesc) { if (tupdesc->tdtypeid == RECORDOID && tupdesc->tdtypmod < 0) assign_record_type_typmod(tupdesc); return tupdesc; /* just for notational convenience */ } /* * TupleDescGetSlot - Initialize a slot based on the supplied tupledesc * * Note: this is obsolete; it is sufficient to call BlessTupleDesc on * the tupdesc. We keep it around just for backwards compatibility with * existing user-written SRFs. */ TupleTableSlot * TupleDescGetSlot(TupleDesc tupdesc) { TupleTableSlot *slot; /* The useful work is here */ BlessTupleDesc(tupdesc); /* Make a standalone slot */ slot = MakeSingleTupleTableSlot(tupdesc); /* Return the slot */ return slot; } /* * TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the * supplied TupleDesc. AttInMetadata can be used in conjunction with C strings * to produce a properly formed tuple. */ AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc) { int natts = tupdesc->natts; int i; Oid atttypeid; Oid attinfuncid; FmgrInfo *attinfuncinfo; Oid *attioparams; int32 *atttypmods; AttInMetadata *attinmeta; attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata)); /* "Bless" the tupledesc so that we can make rowtype datums with it */ attinmeta->tupdesc = BlessTupleDesc(tupdesc); /* * Gather info needed later to call the "in" function for each attribute */ attinfuncinfo = (FmgrInfo *) palloc0(natts * sizeof(FmgrInfo)); attioparams = (Oid *) palloc0(natts * sizeof(Oid)); atttypmods = (int32 *) palloc0(natts * sizeof(int32)); for (i = 0; i < natts; i++) { /* Ignore dropped attributes */ if (!tupdesc->attrs[i]->attisdropped) { atttypeid = tupdesc->attrs[i]->atttypid; getTypeInputInfo(atttypeid, &attinfuncid, &attioparams[i]); fmgr_info(attinfuncid, &attinfuncinfo[i]); atttypmods[i] = tupdesc->attrs[i]->atttypmod; } } attinmeta->attinfuncs = attinfuncinfo; attinmeta->attioparams = attioparams; attinmeta->atttypmods = atttypmods; return attinmeta; } /* * BuildTupleFromCStrings - build a HeapTuple given user data in C string form. * values is an array of C strings, one for each attribute of the return tuple. * A NULL string pointer indicates we want to create a NULL field. */ HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) { TupleDesc tupdesc = attinmeta->tupdesc; int natts = tupdesc->natts; Datum *dvalues; bool *nulls; int i; HeapTuple tuple; dvalues = (Datum *) palloc(natts * sizeof(Datum)); nulls = (bool *) palloc(natts * sizeof(bool)); /* Call the "in" function for each non-dropped attribute */ for (i = 0; i < natts; i++) { if (!tupdesc->attrs[i]->attisdropped) { /* Non-dropped attributes */ dvalues[i] = InputFunctionCall(&attinmeta->attinfuncs[i], values[i], attinmeta->attioparams[i], attinmeta->atttypmods[i]); if (values[i] != NULL) nulls[i] = false; else nulls[i] = true; } else { /* Handle dropped attributes by setting to NULL */ dvalues[i] = (Datum) 0; nulls[i] = true; } } /* * Form a tuple */ tuple = heap_form_tuple(tupdesc, dvalues, nulls); /* * Release locally palloc'd space. XXX would probably be good to pfree * values of pass-by-reference datums, as well. */ pfree(dvalues); pfree(nulls); return tuple; } /* * Functions for sending tuples to the frontend (or other specified destination) * as though it is a SELECT result. These are used by utility commands that * need to project directly to the destination and don't need or want full * table function capability. Currently used by EXPLAIN and SHOW ALL. */ TupOutputState * begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc) { TupOutputState *tstate; tstate = (TupOutputState *) palloc(sizeof(TupOutputState)); tstate->slot = MakeSingleTupleTableSlot(tupdesc); tstate->dest = dest; (*tstate->dest->rStartup) (tstate->dest, (int) CMD_SELECT, tupdesc); return tstate; } /* * write a single tuple */ void do_tup_output(TupOutputState *tstate, Datum *values, bool *isnull) { TupleTableSlot *slot = tstate->slot; int natts = slot->tts_tupleDescriptor->natts; /* make sure the slot is clear */ ExecClearTuple(slot); /* insert data */ memcpy(slot->tts_values, values, natts * sizeof(Datum)); memcpy(slot->tts_isnull, isnull, natts * sizeof(bool)); /* mark slot as containing a virtual tuple */ ExecStoreVirtualTuple(slot); /* send the tuple to the receiver */ (*tstate->dest->receiveSlot) (slot, tstate->dest); /* clean up */ ExecClearTuple(slot); } /* * write a chunk of text, breaking at newline characters * * Should only be used with a single-TEXT-attribute tupdesc. */ void do_text_output_multiline(TupOutputState *tstate, char *text) { Datum values[1]; bool isnull[1] = {false}; while (*text) { char *eol; int len; eol = strchr(text, '\n'); if (eol) { len = eol - text; eol++; } else { len = strlen(text); eol += len; } values[0] = PointerGetDatum(cstring_to_text_with_len(text, len)); do_tup_output(tstate, values, isnull); pfree(DatumGetPointer(values[0])); text = eol; } } void end_tup_output(TupOutputState *tstate) { (*tstate->dest->rShutdown) (tstate->dest); /* note that destroying the dest is not ours to do */ ExecDropSingleTupleTableSlot(tstate->slot); pfree(tstate); }