/*------------------------------------------------------------------------- * * 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-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.84 2005/03/14 04:41:12 tgl Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES * * TABLE CREATE/DELETE * ExecCreateTupleTable - create a new tuple table * ExecDropTupleTable - destroy a table * * SLOT RESERVATION * ExecAllocTableSlot - find an available slot in the table * * SLOT ACCESSORS * ExecStoreTuple - store a tuple in the table * ExecClearTuple - clear contents of a table slot * ExecSetSlotDescriptor - set a slot's tuple descriptor * * SLOT STATUS PREDICATES * TupIsNull - true when slot contains no tuple (macro) * * 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 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 performing 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 ExecProject() and place it into the result tuple slot. * * - ExecutePlan() calls ExecRetrieve() which gets the tuple out of * the slot passed to it (by direct access to slot->val, which is * ugly but not worth changing). 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 */ #include "postgres.h" #include "funcapi.h" #include "access/heapam.h" #include "catalog/pg_type.h" #include "executor/executor.h" #include "parser/parse_expr.h" #include "utils/lsyscache.h" #include "utils/typcache.h" static TupleDesc ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk); /* ---------------------------------------------------------------- * tuple table create/delete functions * ---------------------------------------------------------------- */ /* -------------------------------- * ExecCreateTupleTable * * This creates a new tuple table of the specified size. * * This should be used by InitPlan() to allocate the table. * The table's address will be stored in the EState structure. * -------------------------------- */ TupleTable ExecCreateTupleTable(int tableSize) { TupleTable newtable; int i; /* * sanity checks */ Assert(tableSize >= 1); /* * allocate the table itself */ newtable = (TupleTable) palloc(sizeof(TupleTableData) + (tableSize - 1) * sizeof(TupleTableSlot)); newtable->size = tableSize; newtable->next = 0; /* * initialize all the slots to empty states */ for (i = 0; i < tableSize; i++) { TupleTableSlot *slot = &(newtable->array[i]); slot->type = T_TupleTableSlot; slot->val = NULL; slot->ttc_tupleDescriptor = NULL; slot->ttc_shouldFree = false; slot->ttc_shouldFreeDesc = false; slot->ttc_buffer = InvalidBuffer; slot->ttc_mcxt = CurrentMemoryContext; slot->cache_values = NULL; slot->cache_natts = 0; /* mark slot_getattr state invalid */ } return newtable; } /* -------------------------------- * ExecDropTupleTable * * This frees the storage used by the tuple table itself * and optionally frees 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 */ { /* * sanity checks */ Assert(table != NULL); /* * 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!) */ if (shouldFree) { int next = table->next; int i; for (i = 0; i < next; i++) { TupleTableSlot *slot = &(table->array[i]); ExecClearTuple(slot); if (slot->ttc_shouldFreeDesc) FreeTupleDesc(slot->ttc_tupleDescriptor); if (slot->cache_values) pfree(slot->cache_values); } } /* * finally free the tuple table itself. */ pfree(table); } /* -------------------------------- * MakeTupleTableSlot * * This routine makes an empty standalone TupleTableSlot. * It really shouldn't exist, but there are a few places * that do this, so we may as well centralize the knowledge * of what's in one ... * -------------------------------- */ TupleTableSlot * MakeTupleTableSlot(void) { TupleTableSlot *slot = makeNode(TupleTableSlot); /* This should match ExecCreateTupleTable() */ slot->val = NULL; slot->ttc_tupleDescriptor = NULL; slot->ttc_shouldFree = false; slot->ttc_shouldFreeDesc = false; slot->ttc_buffer = InvalidBuffer; slot->ttc_mcxt = CurrentMemoryContext; slot->cache_values = NULL; slot->cache_natts = 0; /* mark slot_getattr state invalid */ return slot; } /* ---------------------------------------------------------------- * 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 * ExecAllocTableSlot(TupleTable table) { int slotnum; /* new slot number */ /* * sanity checks */ Assert(table != NULL); /* * We expect that the table was made big enough to begin with. * We cannot reallocate it on the fly since previous plan nodes * have already got pointers to individual entries. */ if (table->next >= table->size) elog(ERROR, "plan requires more slots than are available"); slotnum = table->next; table->next++; return &(table->array[slotnum]); } /* ---------------------------------------------------------------- * 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. */ slot->val = tuple; slot->ttc_shouldFree = shouldFree; /* * 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. */ slot->ttc_buffer = buffer; if (BufferIsValid(buffer)) IncrBufferRefCount(buffer); 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 contents of the specified slot if necessary. (Note: * we allow slot->val to be null even when shouldFree is true, because * there are a few callers of ExecStoreTuple that are too lazy to * distinguish whether they are passing a NULL tuple, and always pass * shouldFree = true.) */ if (slot->ttc_shouldFree && slot->val != NULL) heap_freetuple(slot->val); slot->val = NULL; slot->ttc_shouldFree = false; /* * Drop the pin on the referenced buffer, if there is one. */ if (BufferIsValid(slot->ttc_buffer)) ReleaseBuffer(slot->ttc_buffer); slot->ttc_buffer = InvalidBuffer; /* * mark slot_getattr state invalid */ slot->cache_natts = 0; return slot; } /* -------------------------------- * ExecSetSlotDescriptor * * This function is used to set the tuple descriptor associated * with the slot's tuple. * -------------------------------- */ void ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ TupleDesc tupdesc, /* new tuple descriptor */ bool shouldFree) /* is desc owned by slot? */ { if (slot->ttc_shouldFreeDesc) FreeTupleDesc(slot->ttc_tupleDescriptor); slot->ttc_tupleDescriptor = tupdesc; slot->ttc_shouldFreeDesc = shouldFree; /* * mark slot_getattr state invalid */ slot->cache_natts = 0; /* * release any old cache array since tupledesc's natts may have changed */ if (slot->cache_values) pfree(slot->cache_values); slot->cache_values = NULL; } /* ---------------------------------------------------------------- * 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); struct tupleDesc nullTupleDesc; HeapTuple nullTuple; Datum values[1]; char nulls[1]; /* * Since heap_getattr() will treat attributes beyond a tuple's t_natts * as being NULL, we can make an all-nulls tuple just by making it be * of zero length. However, the slot descriptor must match the real * tupType. */ nullTupleDesc = *tupType; nullTupleDesc.natts = 0; nullTuple = heap_formtuple(&nullTupleDesc, values, nulls); ExecSetSlotDescriptor(slot, tupType, false); return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true); } /* ---------------------------------------------------------------- * 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); Resdom *resdom = tle->resdom; if (skipjunk && resdom->resjunk) continue; TupleDescInitEntry(typeInfo, cur_resno++, resdom->resname, resdom->restype, resdom->restypmod, 0); } return typeInfo; } /* * ExecTypeFromExprList - build a tuple descriptor from a list of Exprs * * Here we must make up an arbitrary set of field names. */ TupleDesc ExecTypeFromExprList(List *exprList) { TupleDesc typeInfo; ListCell *l; int cur_resno = 1; char fldname[NAMEDATALEN]; typeInfo = CreateTemplateTupleDesc(list_length(exprList), false); foreach(l, exprList) { Node *e = lfirst(l); sprintf(fldname, "f%d", cur_resno); TupleDescInitEntry(typeInfo, cur_resno++, fldname, exprType(e), exprTypmod(e), 0); } 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 = MakeTupleTableSlot(); /* Bind the tuple description to the slot */ ExecSetSlotDescriptor(slot, tupdesc, true); /* 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. */ HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) { TupleDesc tupdesc = attinmeta->tupdesc; int natts = tupdesc->natts; Datum *dvalues; char *nulls; int i; Oid attioparam; int32 atttypmod; HeapTuple tuple; dvalues = (Datum *) palloc(natts * sizeof(Datum)); nulls = (char *) palloc(natts * sizeof(char)); /* Call the "in" function for each non-null, non-dropped attribute */ for (i = 0; i < natts; i++) { if (!tupdesc->attrs[i]->attisdropped) { /* Non-dropped attributes */ if (values[i] != NULL) { attioparam = attinmeta->attioparams[i]; atttypmod = attinmeta->atttypmods[i]; dvalues[i] = FunctionCall3(&attinmeta->attinfuncs[i], CStringGetDatum(values[i]), ObjectIdGetDatum(attioparam), Int32GetDatum(atttypmod)); nulls[i] = ' '; } else { dvalues[i] = (Datum) 0; nulls[i] = 'n'; } } else { /* Handle dropped attributes by setting to NULL */ dvalues[i] = (Datum) 0; nulls[i] = 'n'; } } /* * Form a tuple */ tuple = heap_formtuple(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->metadata = TupleDescGetAttInMetadata(tupdesc); tstate->dest = dest; (*tstate->dest->rStartup) (tstate->dest, (int) CMD_SELECT, tupdesc); return tstate; } /* * write a single tuple * * values is a list of the external C string representations of the values * to be projected. */ void do_tup_output(TupOutputState *tstate, char **values) { /* build a tuple from the input strings using the tupdesc */ HeapTuple tuple = BuildTupleFromCStrings(tstate->metadata, values); /* send the tuple to the receiver */ (*tstate->dest->receiveTuple) (tuple, tstate->metadata->tupdesc, tstate->dest); /* clean up */ heap_freetuple(tuple); } /* * write a chunk of text, breaking at newline characters * * NB: scribbles on its input! * * Should only be used with a single-TEXT-attribute tupdesc. */ void do_text_output_multiline(TupOutputState *tstate, char *text) { while (*text) { char *eol; eol = strchr(text, '\n'); if (eol) *eol++ = '\0'; else eol = text +strlen(text); do_tup_output(tstate, &text); text = eol; } } void end_tup_output(TupOutputState *tstate) { (*tstate->dest->rShutdown) (tstate->dest); /* note that destroying the dest is not ours to do */ /* XXX worth cleaning up the attinmetadata? */ pfree(tstate); }