/*------------------------------------------------------------------------- * * 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-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.78 2004/05/26 04:41:15 neilc 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 * ExecFetchTuple - fetch a tuple from the table * ExecClearTuple - clear contents of a table slot * ExecSetSlotDescriptor - set a slot's tuple descriptor * ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag * * 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 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 ExecProject() 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 "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 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. Zero out the slots. */ newtable = (TupleTable) palloc(sizeof(TupleTableData)); array = (TupleTableSlot *) palloc0(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 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 */ { 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!) */ if (shouldFree) { for (i = 0; i < next; i++) { ExecClearTuple(&array[i]); if (array[i].ttc_shouldFreeDesc && array[i].ttc_tupleDescriptor != NULL) FreeTupleDesc(array[i].ttc_tupleDescriptor); } } /* * 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 * 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. Fail if it does. * -mer 4 Aug 1992 */ if (table->next >= table->size) elog(ERROR, "plan requires more slots than are available"); /* * 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 = NULL; slot->ttc_shouldFree = true; slot->ttc_descIsNew = true; slot->ttc_shouldFreeDesc = true; slot->ttc_tupleDescriptor = NULL; slot->ttc_buffer = InvalidBuffer; return slot; } /* -------------------------------- * 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 ExecAllocTableSlot() */ slot->val = NULL; slot->ttc_shouldFree = true; slot->ttc_descIsNew = true; slot->ttc_shouldFreeDesc = true; slot->ttc_tupleDescriptor = NULL; slot->ttc_buffer = InvalidBuffer; 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. * * 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 */ { 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 = 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; } /* -------------------------------- * 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 && slot->ttc_tupleDescriptor != NULL) FreeTupleDesc(slot->ttc_tupleDescriptor); slot->ttc_tupleDescriptor = tupdesc; slot->ttc_shouldFreeDesc = shouldFree; } /* -------------------------------- * 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; } /* ---------------------------------------------------------------- * tuple table slot status predicates * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- * 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(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 *attelems; 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)); attelems = (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, &attelems[i]); fmgr_info(attinfuncid, &attinfuncinfo[i]); atttypmods[i] = tupdesc->attrs[i]->atttypmod; } } attinmeta->attinfuncs = attinfuncinfo; attinmeta->attelems = attelems; 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 attelem; 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) { attelem = attinmeta->attelems[i]; atttypmod = attinmeta->atttypmods[i]; dvalues[i] = FunctionCall3(&attinmeta->attinfuncs[i], CStringGetDatum(values[i]), ObjectIdGetDatum(attelem), 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); }