* This information is needed by routines manipulating tuples
* (getattribute, formtuple, etc.).
*
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.46 2001/01/29 00:39:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.64 2003/05/06 00:20:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* TupIsNull - true when slot contains no tuple(Macro)
*
* CONVENIENCE INITIALIZATION ROUTINES
- * ExecInitResultTupleSlot \ convenience routines to initialize
+ * ExecInitResultTupleSlot \ convenience routines to initialize
* ExecInitScanTupleSlot \ the various tuple slots for nodes
* ExecInitExtraTupleSlot / which store copies of tuples.
* ExecInitNullTupleSlot /
*/
#include "postgres.h"
+#include "funcapi.h"
#include "access/heapam.h"
-#include "catalog/pg_type.h"
#include "executor/executor.h"
+#include "utils/lsyscache.h"
/* ----------------------------------------------------------------
TupleTable newtable; /* newly allocated table */
TupleTableSlot *array; /* newly allocated slot array */
- /* ----------------
- * sanity checks
- * ----------------
+ /*
+ * sanity checks
*/
Assert(initialSize >= 1);
- /* ----------------
- * Now allocate our new table along with space for the pointers
- * to the tuples.
+ /*
+ * 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
- * ----------------
+ /*
+ * clean out the slots we just allocated
*/
MemSet(array, 0, initialSize * sizeof(TupleTableSlot));
- /* ----------------
- * initialize the new table and return it to the caller.
- * ----------------
+ /*
+ * initialize the new table and return it to the caller.
*/
newtable->size = initialSize;
newtable->next = 0;
TupleTableSlot *array; /* start of table array */
int i; /* counter */
- /* ----------------
- * sanity checks
- * ----------------
+ /*
+ * sanity checks
*/
Assert(table != NULL);
- /* ----------------
- * get information from the table
- * ----------------
+ /*
+ * 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!)
- * ----------------
+ /*
+ * 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)
{
}
}
- /* ----------------
- * finally free the tuple array and the table itself.
- * ----------------
+ /*
+ * finally free the tuple array and the table itself.
*/
pfree(array);
pfree(table);
int slotnum; /* new slot number */
TupleTableSlot *slot;
- /* ----------------
- * sanity checks
- * ----------------
+ /*
+ * 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
+ /*
+ * 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
- * ----------------
+ * 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"
"\n\tsend 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.
- * ----------------
+ /*
+ * at this point, space in the table is guaranteed so we reserve the
+ * next slot, initialize and return it.
*/
slotnum = table->next;
table->next++;
Buffer buffer,
bool shouldFree)
{
- /* ----------------
- * sanity checks
- * ----------------
+ /*
+ * sanity checks
*/
Assert(slot != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
/* 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.
- * ----------------
+ /*
+ * 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;
{
HeapTuple oldtuple; /* prior contents of slot */
- /* ----------------
- * sanity checks
- * ----------------
+ /*
+ * sanity checks
*/
Assert(slot != NULL);
- /* ----------------
- * get information from the tuple table
- * ----------------
+ /*
+ * get information from the tuple table
*/
oldtuple = slot->val;
- /* ----------------
- * free the old contents of the specified slot if necessary.
- * ----------------
+ /*
+ * free the old contents of the specified slot if necessary.
*/
if (slot->ttc_shouldFree && oldtuple != NULL)
heap_freetuple(oldtuple);
slot->ttc_shouldFree = true; /* probably useless code... */
- /* ----------------
- * Drop the pin on the referenced buffer, if there is one.
- * ----------------
+ /*
+ * Drop the pin on the referenced buffer, if there is one.
*/
if (BufferIsValid(slot->ttc_buffer))
ReleaseBuffer(slot->ttc_buffer);
void
ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
TupleDesc tupdesc, /* new tuple descriptor */
- bool shouldFree) /* is desc owned by slot? */
+ bool shouldFree) /* is desc owned by slot? */
{
if (slot->ttc_shouldFreeDesc &&
slot->ttc_tupleDescriptor != NULL)
* ExecInit{Result,Scan,Extra}TupleSlot
*
* These are convenience routines to initialize the specified slot
- * in nodes inheriting the appropriate state. ExecInitExtraTupleSlot
+ * in nodes inheriting the appropriate state. ExecInitExtraTupleSlot
* is used for initializing special-purpose slots.
* --------------------------------
*/
* ----------------
*/
void
-ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
+ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
{
INIT_SLOT_DEFS;
INIT_SLOT_ALLOC;
- commonstate->cs_ResultTupleSlot = slot;
+ planstate->ps_ResultTupleSlot = slot;
}
/* ----------------
* ----------------
*/
void
-ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
+ExecInitScanTupleSlot(EState *estate, ScanState *scanstate)
{
INIT_SLOT_DEFS;
INIT_SLOT_ALLOC;
- commonscanstate->css_ScanTupleSlot = slot;
+ scanstate->ss_ScanTupleSlot = slot;
}
/* ----------------
TupleTableSlot *
ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
{
- TupleTableSlot* slot = ExecInitExtraTupleSlot(estate);
+ TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
+
/*
* 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.
+ * 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.
*/
HeapTuple nullTuple;
Datum values[1];
* 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
* ----------------------------------------------------------------
*/
TupleDesc
-ExecTypeFromTL(List *targetList)
+ExecTypeFromTL(List *targetList, bool hasoid)
{
- List *tlitem;
TupleDesc typeInfo;
- Resdom *resdom;
- Oid restype;
+ List *tlitem;
int len;
- /* ----------------
- * examine targetlist - if empty then return NULL
- * ----------------
+ /*
+ * allocate a new typeInfo
*/
len = ExecTargetListLength(targetList);
+ typeInfo = CreateTemplateTupleDesc(len, hasoid);
- if (len == 0)
- return NULL;
-
- /* ----------------
- * allocate a new typeInfo
- * ----------------
- */
- typeInfo = CreateTemplateTupleDesc(len);
-
- /* ----------------
+ /*
* scan list, generate type info for each entry
- * ----------------
*/
foreach(tlitem, targetList)
{
TargetEntry *tle = lfirst(tlitem);
+ Resdom *resdom = tle->resdom;
+
+ TupleDescInitEntry(typeInfo,
+ resdom->resno,
+ resdom->resname,
+ resdom->restype,
+ resdom->restypmod,
+ 0,
+ false);
+ }
+
+ return typeInfo;
+}
+
+/*
+ * TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
+ */
+TupleTableSlot *
+TupleDescGetSlot(TupleDesc tupdesc)
+{
+ TupleTableSlot *slot;
+
+ /* Make a standalone slot */
+ slot = MakeTupleTableSlot();
+
+ /* Bind the tuple description to the slot */
+ ExecSetSlotDescriptor(slot, tupdesc, true);
- if (tle->resdom != NULL)
+ /* 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));
+
+ /*
+ * Gather info needed later to call the "in" function for each
+ * attribute
+ */
+ attinfuncinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo));
+ attelems = (Oid *) palloc(natts * sizeof(Oid));
+ atttypmods = (int32 *) palloc(natts * sizeof(int32));
+
+ for (i = 0; i < natts; i++)
+ {
+ atttypeid = tupdesc->attrs[i]->atttypid;
+ getTypeInputInfo(atttypeid, &attinfuncid, &attelems[i]);
+ fmgr_info(attinfuncid, &attinfuncinfo[i]);
+ atttypmods[i] = tupdesc->attrs[i]->atttypmod;
+ }
+ attinmeta->tupdesc = tupdesc;
+ 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 attribute */
+ for (i = 0; i < natts; i++)
+ {
+ if (values[i] != NULL)
{
- resdom = tle->resdom;
- restype = resdom->restype;
-
- TupleDescInitEntry(typeInfo,
- resdom->resno,
- resdom->resname,
- /* fix for SELECT NULL ... */
- (restype ? restype : UNKNOWNOID),
- resdom->restypmod,
- 0,
- false);
-
-#ifdef NOT_USED
- ExecSetTypeInfo(resdom->resno - 1,
- typeInfo,
- (Oid) restype,
- resdom->resno,
- resdom->reslen,
- NameStr(*resdom->resname),
- get_typbyval(restype),
- get_typalign(restype));
-#endif
+ attelem = attinmeta->attelems[i];
+ atttypmod = attinmeta->atttypmods[i];
+
+ dvalues[i] = FunctionCall3(&attinmeta->attinfuncs[i],
+ CStringGetDatum(values[i]),
+ ObjectIdGetDatum(attelem),
+ Int32GetDatum(atttypmod));
+ nulls[i] = ' ';
}
else
{
- /* XXX this branch looks fairly broken ... tgl 12/2000 */
- Resdom *fjRes;
- List *fjTlistP;
- List *fjList = lfirst(tlitem);
-
-#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);
-#ifdef NOT_USED
- ExecSetTypeInfo(fjRes->resno - 1,
- typeInfo,
- (Oid) restype,
- fjRes->resno,
- fjRes->reslen,
- (char *) fjRes->resname,
- get_typbyval(restype),
- get_typalign(restype));
-#endif
-
- foreach(fjTlistP, lnext(fjList))
- {
- TargetEntry *fjTle = lfirst(fjTlistP);
-
- fjRes = fjTle->resdom;
-
- TupleDescInitEntry(typeInfo,
- fjRes->resno,
- fjRes->resname,
- restype,
- fjRes->restypmod,
- 0,
- false);
-
-#ifdef NOT_USED
- ExecSetTypeInfo(fjRes->resno - 1,
- typeInfo,
- (Oid) fjRes->restype,
- fjRes->resno,
- fjRes->reslen,
- (char *) fjRes->resname,
- get_typbyval(fjRes->restype),
- get_typalign(fjRes->restype));
-#endif
- }
+ dvalues[i] = (Datum) 0;
+ nulls[i] = 'n';
}
}
- return typeInfo;
+ /*
+ * 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(CommandDest dest, TupleDesc tupdesc)
+{
+ TupOutputState *tstate;
+
+ tstate = (TupOutputState *) palloc(sizeof(TupOutputState));
+
+ tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
+ tstate->destfunc = DestToFunction(dest);
+
+ (*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT,
+ NULL, tupdesc, NIL);
+
+ 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->destfunc->receiveTuple) (tuple,
+ tstate->metadata->tupdesc,
+ tstate->destfunc);
+ /* 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->destfunc->cleanup) (tstate->destfunc);
+ /* XXX worth cleaning up the attinmetadata? */
+ pfree(tstate);
}