OSDN Git Service

Avoid O(N^2) overhead in repeated nocachegetattr calls when columns of
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 14 Mar 2005 04:41:13 +0000 (04:41 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 14 Mar 2005 04:41:13 +0000 (04:41 +0000)
a tuple are being accessed via ExecEvalVar and the attcacheoff shortcut
isn't usable (due to nulls and/or varlena columns).  To do this, cache
Datums extracted from a tuple in the associated TupleTableSlot.
Also some code cleanup in and around the TupleTable handling.
Atsushi Ogawa with some kibitzing by Tom Lane.

src/backend/access/common/heaptuple.c
src/backend/access/heap/tuptoaster.c
src/backend/executor/execJunk.c
src/backend/executor/execQual.c
src/backend/executor/execTuples.c
src/include/access/heapam.h
src/include/executor/executor.h
src/include/executor/tuptable.h

index 42643a8..22d5e44 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.96 2005/01/27 23:23:49 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.97 2005/03/14 04:41:12 tgl Exp $
  *
  * NOTES
  *       The old interface functions have been converted to macros
@@ -23,6 +23,7 @@
 #include "access/heapam.h"
 #include "access/tuptoaster.h"
 #include "catalog/pg_type.h"
+#include "executor/tuptable.h"
 
 
 /* ----------------------------------------------------------------
@@ -751,6 +752,7 @@ heap_deformtuple(HeapTuple tuple,
                                 char *nulls)
 {
        HeapTupleHeader tup = tuple->t_data;
+       bool            hasnulls = HeapTupleHasNulls(tuple);
        Form_pg_attribute *att = tupleDesc->attrs;
        int                     tdesc_natts = tupleDesc->natts;
        int                     natts;                  /* number of atts to extract */
@@ -775,7 +777,9 @@ heap_deformtuple(HeapTuple tuple,
 
        for (attnum = 0; attnum < natts; attnum++)
        {
-               if (HeapTupleHasNulls(tuple) && att_isnull(attnum, bp))
+               Form_pg_attribute thisatt = att[attnum];
+
+               if (hasnulls && att_isnull(attnum, bp))
                {
                        values[attnum] = (Datum) 0;
                        nulls[attnum] = 'n';
@@ -785,21 +789,21 @@ heap_deformtuple(HeapTuple tuple,
 
                nulls[attnum] = ' ';
 
-               if (!slow && att[attnum]->attcacheoff >= 0)
-                       off = att[attnum]->attcacheoff;
+               if (!slow && thisatt->attcacheoff >= 0)
+                       off = thisatt->attcacheoff;
                else
                {
-                       off = att_align(off, att[attnum]->attalign);
+                       off = att_align(off, thisatt->attalign);
 
                        if (!slow)
-                               att[attnum]->attcacheoff = off;
+                               thisatt->attcacheoff = off;
                }
 
-               values[attnum] = fetchatt(att[attnum], tp + off);
+               values[attnum] = fetchatt(thisatt, tp + off);
 
-               off = att_addlength(off, att[attnum]->attlen, tp + off);
+               off = att_addlength(off, thisatt->attlen, tp + off);
 
-               if (att[attnum]->attlen <= 0)
+               if (thisatt->attlen <= 0)
                        slow = true;            /* can't use attcacheoff anymore */
        }
 
@@ -815,6 +819,177 @@ heap_deformtuple(HeapTuple tuple,
 }
 
 /* ----------------
+ *             slot_deformtuple
+ *
+ *             Given a TupleTableSlot, extract data into cache_values array 
+ *             from the slot's tuple.
+ *
+ *             This is essentially an incremental version of heap_deformtuple:
+ *             on each call we extract attributes up to the one needed, without
+ *             re-computing information about previously extracted attributes.
+ *             slot->cache_natts is the number of attributes already extracted.
+ *
+ *             This only gets called from slot_getattr.  Note that slot_getattr
+ *             must check for a null attribute since we don't create an array
+ *             of null indicators.
+ * ----------------
+ */
+static void
+slot_deformtuple(TupleTableSlot *slot, int natts)
+{
+       HeapTuple               tuple = slot->val;
+       TupleDesc               tupleDesc = slot->ttc_tupleDescriptor;
+       Datum      *values = slot->cache_values;
+       HeapTupleHeader tup = tuple->t_data;
+       bool            hasnulls = HeapTupleHasNulls(tuple);
+       Form_pg_attribute *att = tupleDesc->attrs;
+       int                     attnum;
+       char       *tp;                                 /* ptr to tuple data */
+       long            off;                            /* offset in tuple data */
+       bits8      *bp = tup->t_bits;   /* ptr to null bitmask in tuple */
+       bool            slow;                           /* can we use/set attcacheoff? */
+
+       /*
+        * Check whether the first call for this tuple, and initialize or
+        * restore loop state.
+        */
+       attnum = slot->cache_natts;
+       if (attnum == 0)
+       {
+               /* Start from the first attribute */
+               off = 0;
+               slow = false;
+       }
+       else
+       {
+               /* Restore state from previous execution */
+               off = slot->cache_off;
+               slow = slot->cache_slow;
+       }
+
+       tp = (char *) tup + tup->t_hoff;
+
+       for (; attnum < natts; attnum++)
+       {
+               Form_pg_attribute thisatt = att[attnum];
+
+               if (hasnulls && att_isnull(attnum, bp))
+               {
+                       values[attnum] = (Datum) 0;
+                       slow = true;        /* can't use attcacheoff anymore */
+                       continue;
+               }
+
+               if (!slow && thisatt->attcacheoff >= 0)
+                       off = thisatt->attcacheoff;
+               else
+               {
+                       off = att_align(off, thisatt->attalign);
+
+                       if (!slow)
+                               thisatt->attcacheoff = off;
+               }
+
+               values[attnum] = fetchatt(thisatt, tp + off);
+
+               off = att_addlength(off, thisatt->attlen, tp + off);
+
+               if (thisatt->attlen <= 0)
+                       slow = true;        /* can't use attcacheoff anymore */
+       }
+
+       /*
+        * Save state for next execution
+        */
+       slot->cache_natts = attnum;
+       slot->cache_off = off;
+       slot->cache_slow = slow;
+}
+
+/* --------------------------------
+ *             slot_getattr
+ *
+ *             This function fetches an attribute of the slot's current tuple.
+ *             It is functionally equivalent to heap_getattr, but fetches of
+ *             multiple attributes of the same tuple will be optimized better,
+ *             because we avoid O(N^2) behavior from multiple calls of
+ *             nocachegetattr(), even when attcacheoff isn't usable.
+ * --------------------------------
+ */
+Datum
+slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+       HeapTuple               tuple = slot->val;
+       TupleDesc               tupleDesc = slot->ttc_tupleDescriptor;
+       HeapTupleHeader tup;
+
+       /*
+        * system attributes are handled by heap_getsysattr
+        */
+       if (attnum <= 0)
+               return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
+
+       /*
+        * check if attnum is out of range according to either the tupdesc
+        * or the tuple itself; if so return NULL
+        */
+       tup = tuple->t_data;
+
+       if (attnum > tup->t_natts || attnum > tupleDesc->natts)
+       {
+               *isnull = true;
+               return (Datum) 0;
+       }
+
+       /*
+        * check if target attribute is null
+        */
+       if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
+       {
+               *isnull = true;
+               return (Datum) 0;
+       }
+
+       /*
+        * If the attribute's column has been dropped, we force a NULL
+        * result. This case should not happen in normal use, but it could
+        * happen if we are executing a plan cached before the column was
+        * dropped.
+        */
+       if (tupleDesc->attrs[attnum - 1]->attisdropped)
+       {
+               *isnull = true;
+               return (Datum) 0;
+       }
+
+       /*
+        * If attribute wasn't already extracted, extract it and preceding
+        * attributes.
+        */
+       if (attnum > slot->cache_natts)
+       {
+               /*
+                * If first time for this TupleTableSlot, allocate the cache
+                * workspace.  It must have the same lifetime as the slot, so allocate
+                * it in the slot's own context.  We size the array according to what
+                * the tupdesc says, NOT the tuple.
+                */
+               if (slot->cache_values == NULL)
+                       slot->cache_values = (Datum *)
+                               MemoryContextAlloc(slot->ttc_mcxt,
+                                                                  tupleDesc->natts * sizeof(Datum));
+
+               slot_deformtuple(slot, attnum);
+       }
+
+       /*
+        * The result is acquired from cache_values array.
+        */
+       *isnull = false;
+       return slot->cache_values[attnum - 1];
+}
+
+/* ----------------
  *             heap_freetuple
  * ----------------
  */
index c06b3b8..44ed126 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.47 2005/01/01 05:43:06 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.48 2005/03/14 04:41:12 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1197,9 +1197,9 @@ toast_fetch_datum(varattrib *attr)
                /*
                 * Have a chunk, extract the sequence number and the data
                 */
-               residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull));
+               residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
                Assert(!isnull);
-               chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull));
+               chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
                Assert(!isnull);
                chunksize = VARATT_SIZE(chunk) - VARHDRSZ;
 
@@ -1372,9 +1372,9 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
                /*
                 * Have a chunk, extract the sequence number and the data
                 */
-               residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull));
+               residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
                Assert(!isnull);
-               chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull));
+               chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
                Assert(!isnull);
                chunksize = VARATT_SIZE(chunk) - VARHDRSZ;
 
index 5087c7b..f747976 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.46 2004/12/31 21:59:45 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.47 2005/03/14 04:41:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -209,20 +209,13 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
                                         Datum *value,
                                         bool *isNull)
 {
-       List       *targetList;
        ListCell   *t;
-       AttrNumber      resno;
-       TupleDesc       tupType;
-       HeapTuple       tuple;
 
        /*
-        * first look in the junkfilter's target list for an attribute with
+        * Look in the junkfilter's target list for an attribute with
         * the given name
         */
-       resno = InvalidAttrNumber;
-       targetList = junkfilter->jf_targetList;
-
-       foreach(t, targetList)
+       foreach(t, junkfilter->jf_targetList)
        {
                TargetEntry *tle = lfirst(t);
                Resdom     *resdom = tle->resdom;
@@ -231,26 +224,13 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
                        (strcmp(resdom->resname, attrName) == 0))
                {
                        /* We found it ! */
-                       resno = resdom->resno;
-                       break;
+                       *value = slot_getattr(slot, resdom->resno, isNull);
+                       return true;
                }
        }
 
-       if (resno == InvalidAttrNumber)
-       {
-               /* Ooops! We couldn't find this attribute... */
-               return false;
-       }
-
-       /*
-        * Now extract the attribute value from the tuple.
-        */
-       tuple = slot->val;
-       tupType = slot->ttc_tupleDescriptor;
-
-       *value = heap_getattr(tuple, resno, tupType, isNull);
-
-       return true;
+       /* Ooops! We couldn't find this attribute... */
+       return false;
 }
 
 /*
index 0199df0..87cc201 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.171 2004/12/31 21:59:45 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.172 2005/03/14 04:41:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -438,11 +438,8 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                        bool *isNull, ExprDoneCond *isDone)
 {
        Var                *variable = (Var *) exprstate->expr;
-       Datum           result;
        TupleTableSlot *slot;
        AttrNumber      attnum;
-       HeapTuple       heapTuple;
-       TupleDesc       tuple_type;
 
        if (isDone)
                *isDone = ExprSingleResult;
@@ -475,35 +472,19 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                        break;
        }
 
-       /*
-        * extract tuple information from the slot
-        */
-       heapTuple = slot->val;
-       tuple_type = slot->ttc_tupleDescriptor;
-
+#ifdef USE_ASSERT_CHECKING
        /*
         * Some checks that are only applied for user attribute numbers (bogus
-        * system attnums will be caught inside heap_getattr).
+        * system attnums will be caught inside slot_getattr).
         */
        if (attnum > 0)
        {
-               /*
-                * This assert checks that the attnum is valid.
-                */
-               Assert(attnum <= tuple_type->natts &&
-                          tuple_type->attrs[attnum - 1] != NULL);
+               TupleDesc       tuple_type = slot->ttc_tupleDescriptor;
 
                /*
-                * If the attribute's column has been dropped, we force a NULL
-                * result. This case should not happen in normal use, but it could
-                * happen if we are executing a plan cached before the column was
-                * dropped.
+                * This assert checks that the attnum is valid.
                 */
-               if (tuple_type->attrs[attnum - 1]->attisdropped)
-               {
-                       *isNull = true;
-                       return (Datum) 0;
-               }
+               Assert(attnum <= tuple_type->natts);
 
                /*
                 * This assert checks that the datatype the plan expects to get
@@ -515,16 +496,12 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                 * Note that we can't check dropped columns, since their atttypid has
                 * been zeroed.
                 */
-               Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid);
+               Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid ||
+                          tuple_type->attrs[attnum - 1]->attisdropped);
        }
+#endif /* USE_ASSERT_CHECKING */
 
-       result = heap_getattr(heapTuple,        /* tuple containing attribute */
-                                                 attnum,               /* attribute number of desired
-                                                                                * attribute */
-                                                 tuple_type,   /* tuple descriptor of tuple */
-                                                 isNull);              /* return: is attribute null? */
-
-       return result;
+       return slot_getattr(slot, attnum, isNull);
 }
 
 /* ----------------------------------------------------------------
index 2864589..8574efc 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.83 2004/12/31 21:59:45 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.84 2005/03/14 04:41:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  *
  *      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)
+ *             TupIsNull                               - true when slot contains no tuple (macro)
  *
  *      CONVENIENCE INITIALIZATION ROUTINES
  *             ExecInitResultTupleSlot    \    convenience routines to initialize
@@ -60,7 +58,7 @@
  *             - 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.
+ *               tuples resulting from performing target list projections.
  *
  *             During ExecRun()
  *             ----------------
@@ -71,8 +69,8 @@
  *               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.
+ *               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()
  *             ----------------
  *             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"
 
@@ -124,43 +105,52 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList,
  *                               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 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                                             /* return: address of table */
-ExecCreateTupleTable(int initialSize)  /* initial number of slots in
-                                                                                * table */
+TupleTable
+ExecCreateTupleTable(int tableSize)
 {
-       TupleTable      newtable;               /* newly allocated table */
-       TupleTableSlot *array;          /* newly allocated slot array */
+       TupleTable      newtable;
+       int                     i;
 
        /*
         * sanity checks
         */
-       Assert(initialSize >= 1);
+       Assert(tableSize >= 1);
 
        /*
-        * Now allocate our new table along with space for the pointers to the
-        * tuples.      Zero out the slots.
+        * allocate the table itself
         */
-
-       newtable = (TupleTable) palloc(sizeof(TupleTableData));
-       array = (TupleTableSlot *) palloc0(initialSize * sizeof(TupleTableSlot));
+       newtable = (TupleTable) palloc(sizeof(TupleTableData) +
+                                                                  (tableSize - 1) * sizeof(TupleTableSlot));
+       newtable->size = tableSize;
+       newtable->next = 0;
 
        /*
-        * initialize the new table and return it to the caller.
+        * initialize all the slots to empty states
         */
-       newtable->size = initialSize;
-       newtable->next = 0;
-       newtable->array = array;
+       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;
 }
@@ -178,22 +168,12 @@ 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
@@ -201,27 +181,60 @@ ExecDropTupleTable(TupleTable table,      /* tuple table */
         */
        if (shouldFree)
        {
+               int                     next = table->next;
+               int                     i;
+
                for (i = 0; i < next; i++)
                {
-                       ExecClearTuple(&array[i]);
-                       if (array[i].ttc_shouldFreeDesc &&
-                               array[i].ttc_tupleDescriptor != NULL)
-                               FreeTupleDesc(array[i].ttc_tupleDescriptor);
+                       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 array and the table itself.
+        * finally free the tuple table itself.
         */
-       pfree(array);
        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
  *
@@ -236,7 +249,6 @@ TupleTableSlot *
 ExecAllocTableSlot(TupleTable table)
 {
        int                     slotnum;                /* new slot number */
-       TupleTableSlot *slot;
 
        /*
         * sanity checks
@@ -244,66 +256,17 @@ ExecAllocTableSlot(TupleTable table)
        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
+        * 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");
 
-       /*
-        * 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;
+       return &(table->array[slotnum]);
 }
 
 /* ----------------------------------------------------------------
@@ -356,21 +319,22 @@ ExecStoreTuple(HeapTuple tuple,
        /* 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 */
+       /*
+        * 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.
         */
        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.
+        * a pointer into it.  We assume the caller already has such a pin.
         */
+       slot->ttc_buffer = buffer;
        if (BufferIsValid(buffer))
                IncrBufferRefCount(buffer);
 
@@ -388,27 +352,23 @@ ExecStoreTuple(HeapTuple tuple,
 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.
+        * 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 && oldtuple != NULL)
-               heap_freetuple(oldtuple);
+       if (slot->ttc_shouldFree && slot->val != NULL)
+               heap_freetuple(slot->val);
 
        slot->val = NULL;
-
-       slot->ttc_shouldFree = true;    /* probably useless code... */
+       slot->ttc_shouldFree = false;
 
        /*
         * Drop the pin on the referenced buffer, if there is one.
@@ -418,6 +378,11 @@ ExecClearTuple(TupleTableSlot *slot)       /* slot in which to store tuple */
 
        slot->ttc_buffer = InvalidBuffer;
 
+       /*
+        * mark slot_getattr state invalid
+        */
+       slot->cache_natts = 0;
+
        return slot;
 }
 
@@ -433,36 +398,31 @@ 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)
+       if (slot->ttc_shouldFreeDesc)
                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;
+       /*
+        * 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;
 }
 
-/* ----------------------------------------------------------------
- *                               tuple table slot status predicates
- * ----------------------------------------------------------------
- */
 
 /* ----------------------------------------------------------------
  *                             convenience initialization routines
  * ----------------------------------------------------------------
  */
+
 /* --------------------------------
  *             ExecInit{Result,Scan,Extra}TupleSlot
  *
index 6fa3bbf..3b73d5e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.94 2005/01/27 23:24:11 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.95 2005/03/14 04:41:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,9 +40,6 @@
  * ----------------
  */
 
-extern Datum nocachegetattr(HeapTuple tup, int attnum,
-                          TupleDesc att, bool *isnull);
-
 #if !defined(DISABLE_COMPLEX_MACRO)
 
 #define fastgetattr(tup, attnum, tupleDesc, isnull)                                    \
@@ -115,9 +112,6 @@ extern Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
        ) \
 )
 
-extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
-                               bool *isnull);
-
 
 /* ----------------
  *             function prototypes for heap access method
@@ -191,6 +185,8 @@ extern void DataFill(char *data, TupleDesc tupleDesc,
 extern int     heap_attisnull(HeapTuple tup, int attnum);
 extern Datum nocachegetattr(HeapTuple tup, int attnum,
                           TupleDesc att, bool *isnull);
+extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
+                               bool *isnull);
 extern HeapTuple heap_copytuple(HeapTuple tuple);
 extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
 extern HeapTuple heap_formtuple(TupleDesc tupleDescriptor,
index f07bdd8..8eb3576 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.115 2004/12/31 22:03:29 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.116 2005/03/14 04:41:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -158,10 +158,10 @@ extern void ExecAssignScanProjectionInfo(ScanState *node);
 /*
  * prototypes from functions in execTuples.c
  */
-extern TupleTable ExecCreateTupleTable(int initialSize);
+extern TupleTable ExecCreateTupleTable(int tableSize);
 extern void ExecDropTupleTable(TupleTable table, bool shouldFree);
-extern TupleTableSlot *ExecAllocTableSlot(TupleTable table);
 extern TupleTableSlot *MakeTupleTableSlot(void);
+extern TupleTableSlot *ExecAllocTableSlot(TupleTable table);
 extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
                           TupleTableSlot *slot,
                           Buffer buffer,
@@ -169,7 +169,6 @@ extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
 extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
 extern void ExecSetSlotDescriptor(TupleTableSlot *slot,
                                          TupleDesc tupdesc, bool shouldFree);
-extern void ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, bool isNew);
 extern void ExecInitResultTupleSlot(EState *estate, PlanState *planstate);
 extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate);
 extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
index e496a66..9079136 100644 (file)
@@ -7,11 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.26 2004/12/31 22:03:29 pgsql Exp $
- *
- * NOTES
- *       The tuple table interface is getting pretty ugly.
- *       It should be redesigned soon.
+ * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.27 2005/03/14 04:41:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "access/htup.h"
 
-/* ----------------
- *             The executor tuple table is managed and manipulated by special
- *             code in executor/execTuples.c.
- *
- *             TupleTableSlot information
- *
- *                     val                                     current tuple, or NULL if no tuple
- *                     shouldFree                      boolean - should we pfree() tuple
- *                     descIsNew                       boolean - true when tupleDescriptor changes
- *                     tupleDescriptor         type information for the tuple data
- *                     shouldFreeDesc          boolean - should we free tupleDescriptor
- *                     buffer                          the buffer for tuples pointing to disk pages
- *
- *             The executor stores pointers to tuples in a ``tuple table''
- *             which is composed of TupleTableSlots.  Sometimes the tuples
- *             are pointers to buffer pages, while others are pointers to
- *             palloc'ed memory; the shouldFree variable tells us when
- *             we may call pfree() on a tuple.  -cim 9/23/90
- *
- *             If buffer is not InvalidBuffer, then the slot is holding a pin
- *             on the indicated buffer page; drop the pin when we release the
- *             slot's reference to that buffer.
- *
- *             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.  -cim 1/18/90
+
+/*
+ * The executor stores pointers to tuples in a "tuple table"
+ * which is composed of TupleTableSlots.  Sometimes the tuples
+ * are pointers to buffer pages, while others are pointers to
+ * palloc'ed memory; the shouldFree variable tells us whether
+ * we may call pfree() on a tuple.  When shouldFree is true,
+ * the tuple is "owned" by the TupleTableSlot and should be
+ * freed when the slot's reference to the tuple is dropped.
  *
- *             shouldFreeDesc is similar to shouldFree: if it's true, then the
- *             tupleDescriptor is "owned" by the TupleTableSlot and should be
- *             freed when the slot's reference to the descriptor is dropped.
+ * shouldFreeDesc is similar to shouldFree: if it's true, then the
+ * tupleDescriptor is "owned" by the TupleTableSlot and should be
+ * freed when the slot's reference to the descriptor is dropped.
  *
- *             See executor.h for decls of functions defined in execTuples.c
- *             -jolly
+ * If buffer is not InvalidBuffer, then the slot is holding a pin
+ * on the indicated buffer page; drop the pin when we release the
+ * slot's reference to that buffer.  (shouldFree should always be
+ * false in such a case, since presumably val is pointing at the
+ * buffer page.)
  *
- * ----------------
+ * The slot_getattr() routine allows extraction of attribute values from
+ * a TupleTableSlot's current tuple.  It is equivalent to heap_getattr()
+ * except that it can optimize fetching of multiple values more efficiently.
+ * The cache_xxx fields of TupleTableSlot are support for slot_getattr().
  */
 typedef struct TupleTableSlot
 {
-       NodeTag         type;
-       HeapTuple       val;
-       bool            ttc_shouldFree;
-       bool            ttc_descIsNew;
-       bool            ttc_shouldFreeDesc;
-       TupleDesc       ttc_tupleDescriptor;
-       Buffer          ttc_buffer;
+       NodeTag         type;                   /* vestigial ... allows IsA tests */
+       HeapTuple       val;                    /* current tuple, or NULL if none */
+       TupleDesc       ttc_tupleDescriptor;    /* tuple's descriptor */
+       bool            ttc_shouldFree;                 /* should pfree tuple? */
+       bool            ttc_shouldFreeDesc;             /* should pfree descriptor? */
+       Buffer          ttc_buffer;             /* tuple's buffer, or InvalidBuffer */
+       MemoryContext ttc_mcxt;         /* slot itself is in this context */
+       Datum      *cache_values;       /* currently extracted values */
+       int                     cache_natts;    /* # of valid values in cache_values */
+       bool            cache_slow;             /* saved state for slot_getattr */
+       long            cache_off;              /* saved state for slot_getattr */
 } TupleTableSlot;
 
-/* ----------------
- *             tuple table data structure
- * ----------------
+/*
+ * Tuple table data structure: an array of TupleTableSlots.
  */
 typedef struct TupleTableData
 {
-       int                     size;                   /* size of the table */
+       int                     size;                   /* size of the table (number of slots) */
        int                     next;                   /* next available slot number */
-       TupleTableSlot *array;          /* array of TupleTableSlot's */
-} TupleTableData;
+       TupleTableSlot array[1];        /* VARIABLE LENGTH ARRAY - must be last */
+} TupleTableData;                              /* VARIABLE LENGTH STRUCT */
 
 typedef TupleTableData *TupleTable;
 
+
+/* in access/common/heaptuple.c */
+extern Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull);
+
 #endif   /* TUPTABLE_H */