Avoid O(N^2) overhead in repeated nocachegetattr calls when columns of
authorTom Lane
Mon, 14 Mar 2005 04:41:13 +0000 (04:41 +0000)
committerTom Lane
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 42643a87529bf3ea67c46f6fac0cc0e16e14e150..22d5e44dc3f01e5b2bb38295d287610680564713 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 */
    }
 
@@ -814,6 +818,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 c06b3b8d36ae9c312321d0134902c08225ef484d..44ed126fca2df884e7631786c7465603391a2545 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 5087c7bfc3941e94fec1163cd2ec4339f36d4d0b..f747976acfae2dc54941723b268a57d8fb46c10a 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 0199df0f719139c8ed379ef53f75a621f930941c..87cc201a3e3cf5412c2ebd4d3a523415b880bbdf 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 2864589ebb368761104b90647267b51a3fc22d8c..8574efc95d83687169bb74dd6ba3b94d1551d358 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,21 +168,11 @@ 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
@@ -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 6fa3bbf455039634d92cb32cb3141f6b906717ef..3b73d5ea8a1a8abf28c360878a67da37eb46b07b 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 f07bdd89d182a192ce0fb179f5364caffd256757..8eb357636ad763d4fcf287388eefd6676091b105 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 e496a666ce482c582253a3ea574be6a1c3b6396e..90791366ffb20a60921d46859c0665fd2333e56f 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 */