]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/execTuples.c
Implement feature of new FE/BE protocol whereby RowDescription identifies
[postgresql] / src / backend / executor / execTuples.c
index e5f1a269d817bb8bd091f31114fab638b66d28e2..c81dd33d36a2547dc7f83ee5bca36e435ca47504 100644 (file)
  *       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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,7 +40,7 @@
  *             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"
 
 
 /* ----------------------------------------------------------------
@@ -134,29 +135,26 @@ ExecCreateTupleTable(int initialSize)     /* initial number of slots in
        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;
@@ -182,25 +180,22 @@ ExecDropTupleTable(TupleTable table,      /* tuple table */
        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)
        {
@@ -213,9 +208,8 @@ ExecDropTupleTable(TupleTable table,        /* tuple table */
                }
        }
 
-       /* ----------------
-        *      finally free the tuple array and the table itself.
-        * ----------------
+       /*
+        * finally free the tuple array and the table itself.
         */
        pfree(array);
        pfree(table);
@@ -242,36 +236,32 @@ ExecAllocTableSlot(TupleTable 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++;
@@ -358,9 +348,8 @@ ExecStoreTuple(HeapTuple tuple,
                           Buffer buffer,
                           bool shouldFree)
 {
-       /* ----------------
-        *      sanity checks
-        * ----------------
+       /*
+        * sanity checks
         */
        Assert(slot != NULL);
        /* passing shouldFree=true for a tuple on a disk page is not sane */
@@ -369,10 +358,9 @@ ExecStoreTuple(HeapTuple tuple,
        /* 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;
@@ -401,21 +389,18 @@ ExecClearTuple(TupleTableSlot *slot)      /* slot in which to store tuple */
 {
        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);
@@ -424,9 +409,8 @@ ExecClearTuple(TupleTableSlot *slot)        /* slot in which to store tuple */
 
        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);
@@ -446,7 +430,7 @@ ExecClearTuple(TupleTableSlot *slot)        /* slot in which to store tuple */
 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)
@@ -482,7 +466,7 @@ ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,            /* slot to change */
  *             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.
  * --------------------------------
  */
@@ -499,11 +483,11 @@ ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,          /* slot to change */
  * ----------------
  */
 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;
 }
 
 /* ----------------
@@ -511,11 +495,11 @@ ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
  * ----------------
  */
 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;
 }
 
 /* ----------------
@@ -541,11 +525,13 @@ ExecInitExtraTupleSlot(EState *estate)
 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];
@@ -564,6 +550,7 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
  *             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
@@ -572,123 +559,224 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
  * ----------------------------------------------------------------
  */
 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);
 }