Resurrect heap_deformtuple(), this time implemented as a singly nested
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Jun 2004 20:35:21 +0000 (20:35 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Jun 2004 20:35:21 +0000 (20:35 +0000)
loop over the fields instead of a loop around heap_getattr.  This is
considerably faster (O(N) instead of O(N^2)) when there are nulls or
varlena fields, since those prevent use of attcacheoff.  Replace loops
over heap_getattr with heap_deformtuple in situations where all or most
of the fields have to be fetched, such as printtup and tuptoaster.
Profiling done more than a year ago shows that this should be a nice
win for situations involving many-column tables.

src/backend/access/common/heaptuple.c
src/backend/access/common/printtup.c
src/backend/access/heap/tuptoaster.c
src/backend/commands/tablecmds.c
src/backend/commands/typecmds.c
src/backend/executor/execJunk.c
src/backend/executor/spi.c
src/include/access/heapam.h

index 4355c0df4c704b744daca974b92f3c445dbbfaef..266cf3bdcc2178a641f59555e1a50fa9c59ea9bb 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.90 2004/04/01 21:28:43 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.91 2004/06/04 20:35:21 tgl Exp $
  *
  * NOTES
  *   The old interface functions have been converted to macros
  */
 Size
 ComputeDataSize(TupleDesc tupleDesc,
-               Datum *value,
+               Datum *values,
                char *nulls)
 {
-   uint32      data_length = 0;
+   Size        data_length = 0;
    int         i;
    int         numberOfAttributes = tupleDesc->natts;
    Form_pg_attribute *att = tupleDesc->attrs;
@@ -51,7 +51,7 @@ ComputeDataSize(TupleDesc tupleDesc,
            continue;
 
        data_length = att_align(data_length, att[i]->attalign);
-       data_length = att_addlength(data_length, att[i]->attlen, value[i]);
+       data_length = att_addlength(data_length, att[i]->attlen, values[i]);
    }
 
    return data_length;
@@ -59,19 +59,20 @@ ComputeDataSize(TupleDesc tupleDesc,
 
 /* ----------------
  *     DataFill
+ *
+ * Load data portion of a tuple from values/nulls arrays
  * ----------------
  */
 void
 DataFill(char *data,
         TupleDesc tupleDesc,
-        Datum *value,
+        Datum *values,
         char *nulls,
         uint16 *infomask,
         bits8 *bit)
 {
-   bits8      *bitP = 0;
-   int         bitmask = 0;
-   Size        data_length;
+   bits8      *bitP;
+   int         bitmask;
    int         i;
    int         numberOfAttributes = tupleDesc->natts;
    Form_pg_attribute *att = tupleDesc->attrs;
@@ -81,11 +82,19 @@ DataFill(char *data,
        bitP = &bit[-1];
        bitmask = CSIGNBIT;
    }
+   else
+   {
+       /* just to keep compiler quiet */
+       bitP = NULL;
+       bitmask = 0;
+   }
 
    *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
 
    for (i = 0; i < numberOfAttributes; i++)
    {
+       Size        data_length;
+
        if (bit != NULL)
        {
            if (bitmask != CSIGNBIT)
@@ -112,33 +121,33 @@ DataFill(char *data,
        if (att[i]->attbyval)
        {
            /* pass-by-value */
-           store_att_byval(data, value[i], att[i]->attlen);
+           store_att_byval(data, values[i], att[i]->attlen);
            data_length = att[i]->attlen;
        }
        else if (att[i]->attlen == -1)
        {
            /* varlena */
            *infomask |= HEAP_HASVARWIDTH;
-           if (VARATT_IS_EXTERNAL(value[i]))
+           if (VARATT_IS_EXTERNAL(values[i]))
                *infomask |= HEAP_HASEXTERNAL;
-           if (VARATT_IS_COMPRESSED(value[i]))
+           if (VARATT_IS_COMPRESSED(values[i]))
                *infomask |= HEAP_HASCOMPRESSED;
-           data_length = VARATT_SIZE(DatumGetPointer(value[i]));
-           memcpy(data, DatumGetPointer(value[i]), data_length);
+           data_length = VARATT_SIZE(DatumGetPointer(values[i]));
+           memcpy(data, DatumGetPointer(values[i]), data_length);
        }
        else if (att[i]->attlen == -2)
        {
            /* cstring */
            *infomask |= HEAP_HASVARWIDTH;
-           data_length = strlen(DatumGetCString(value[i])) + 1;
-           memcpy(data, DatumGetPointer(value[i]), data_length);
+           data_length = strlen(DatumGetCString(values[i])) + 1;
+           memcpy(data, DatumGetPointer(values[i]), data_length);
        }
        else
        {
            /* fixed-length pass-by-reference */
            Assert(att[i]->attlen > 0);
            data_length = att[i]->attlen;
-           memcpy(data, DatumGetPointer(value[i]), data_length);
+           memcpy(data, DatumGetPointer(values[i]), data_length);
        }
 
        data += data_length;
@@ -160,27 +169,28 @@ heap_attisnull(HeapTuple tup, int attnum)
    if (attnum > (int) tup->t_data->t_natts)
        return 1;
 
-   if (HeapTupleNoNulls(tup))
-       return 0;
-
    if (attnum > 0)
+   {
+       if (HeapTupleNoNulls(tup))
+           return 0;
        return att_isnull(attnum - 1, tup->t_data->t_bits);
-   else
-       switch (attnum)
-       {
-           case TableOidAttributeNumber:
-           case SelfItemPointerAttributeNumber:
-           case ObjectIdAttributeNumber:
-           case MinTransactionIdAttributeNumber:
-           case MinCommandIdAttributeNumber:
-           case MaxTransactionIdAttributeNumber:
-           case MaxCommandIdAttributeNumber:
-               /* these are never null */
-               break;
-
-           default:
-               elog(ERROR, "invalid attnum: %d", attnum);
-       }
+   }
+
+   switch (attnum)
+   {
+       case TableOidAttributeNumber:
+       case SelfItemPointerAttributeNumber:
+       case ObjectIdAttributeNumber:
+       case MinTransactionIdAttributeNumber:
+       case MinCommandIdAttributeNumber:
+       case MaxTransactionIdAttributeNumber:
+       case MaxCommandIdAttributeNumber:
+           /* these are never null */
+           break;
+
+       default:
+           elog(ERROR, "invalid attnum: %d", attnum);
+   }
 
    return 0;
 }
@@ -202,6 +212,8 @@ heap_attisnull(HeapTuple tup, int attnum)
  *     perform well for queries which hit large #'s of tuples.  After
  *     you cache the offsets once, examining all the other tuples using
  *     the same attribute descriptor will go much quicker. -cim 5/4/91
+ *
+ *     NOTE: if you need to change this code, see also heap_deformtuple.
  * ----------------
  */
 Datum
@@ -536,53 +548,18 @@ heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest)
    memcpy((char *) dest->t_data, (char *) src->t_data, src->t_len);
 }
 
-#ifdef NOT_USED
-/* ----------------
- *     heap_deformtuple
- *
- *     the inverse of heap_formtuple (see below)
- * ----------------
- */
-void
-heap_deformtuple(HeapTuple tuple,
-                TupleDesc tdesc,
-                Datum *values,
-                char *nulls)
-{
-   int         i;
-   int         natts;
-
-   Assert(HeapTupleIsValid(tuple));
-
-   natts = tuple->t_natts;
-   for (i = 0; i < natts; i++)
-   {
-       bool        isnull;
-
-       values[i] = heap_getattr(tuple,
-                                i + 1,
-                                tdesc,
-                                &isnull);
-       if (isnull)
-           nulls[i] = 'n';
-       else
-           nulls[i] = ' ';
-   }
-}
-#endif
-
 /* ----------------
  *     heap_formtuple
  *
- *     constructs a tuple from the given *value and *nulls arrays
+ *     construct a tuple from the given values[] and nulls[] arrays
  *
  *     Null attributes are indicated by a 'n' in the appropriate byte
- *     of *nulls.  Non-null attributes are indicated by a ' ' (space).
+ *     of nulls[]. Non-null attributes are indicated by a ' ' (space).
  * ----------------
  */
 HeapTuple
 heap_formtuple(TupleDesc tupleDescriptor,
-              Datum *value,
+              Datum *values,
               char *nulls)
 {
    HeapTuple   tuple;          /* return tuple */
@@ -621,7 +598,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
 
    hoff = len = MAXALIGN(len); /* align user data safely */
 
-   len += ComputeDataSize(tupleDescriptor, value, nulls);
+   len += ComputeDataSize(tupleDescriptor, values, nulls);
 
    /*
     * Allocate and zero the space needed.  Note that the tuple body and
@@ -651,7 +628,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
 
    DataFill((char *) td + hoff,
             tupleDescriptor,
-            value,
+            values,
             nulls,
             &td->t_infomask,
             (hasnull ? td->t_bits : NULL));
@@ -664,68 +641,59 @@ heap_formtuple(TupleDesc tupleDescriptor,
  *
  *     forms a new tuple from an old tuple and a set of replacement values.
  *     returns a new palloc'ed tuple.
+ *
+ *     XXX it is misdesign that this is passed a Relation and not just a
+ *     TupleDesc to describe the tuple structure.
  * ----------------
  */
 HeapTuple
 heap_modifytuple(HeapTuple tuple,
                 Relation relation,
-                Datum *replValue,
-                char *replNull,
-                char *repl)
+                Datum *replValues,
+                char *replNulls,
+                char *replActions)
 {
+   TupleDesc   tupleDesc = RelationGetDescr(relation);
+   int         numberOfAttributes = tupleDesc->natts;
    int         attoff;
-   int         numberOfAttributes;
-   Datum      *value;
+   Datum      *values;
    char       *nulls;
-   bool        isNull;
    HeapTuple   newTuple;
 
    /*
-    * sanity checks
-    */
-   Assert(HeapTupleIsValid(tuple));
-   Assert(RelationIsValid(relation));
-   Assert(PointerIsValid(replValue));
-   Assert(PointerIsValid(replNull));
-   Assert(PointerIsValid(repl));
-
-   numberOfAttributes = RelationGetForm(relation)->relnatts;
-
-   /*
-    * allocate and fill *value and *nulls arrays from either the tuple or
+    * allocate and fill values and nulls arrays from either the tuple or
     * the repl information, as appropriate.
+    *
+    * NOTE: it's debatable whether to use heap_deformtuple() here or
+    * just heap_getattr() only the non-replaced colums.  The latter could
+    * win if there are many replaced columns and few non-replaced ones.
+    * However, heap_deformtuple costs only O(N) while the heap_getattr
+    * way would cost O(N^2) if there are many non-replaced columns, so it
+    * seems better to err on the side of linear cost.
     */
-   value = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
+   values = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
    nulls = (char *) palloc(numberOfAttributes * sizeof(char));
 
+   heap_deformtuple(tuple, tupleDesc, values, nulls);
+
    for (attoff = 0; attoff < numberOfAttributes; attoff++)
    {
-       if (repl[attoff] == ' ')
+       if (replActions[attoff] == 'r')
        {
-           value[attoff] = heap_getattr(tuple,
-                                        AttrOffsetGetAttrNumber(attoff),
-                                        RelationGetDescr(relation),
-                                        &isNull);
-           nulls[attoff] = (isNull) ? 'n' : ' ';
-
+           values[attoff] = replValues[attoff];
+           nulls[attoff] = replNulls[attoff];
        }
-       else if (repl[attoff] == 'r')
-       {
-           value[attoff] = replValue[attoff];
-           nulls[attoff] = replNull[attoff];
-       }
-       else
-           elog(ERROR, "unrecognized replace flag: %d", (int) repl[attoff]);
+       else if (replActions[attoff] != ' ')
+           elog(ERROR, "unrecognized replace flag: %d",
+                (int) replActions[attoff]);
    }
 
    /*
-    * create a new tuple from the *values and *nulls arrays
+    * create a new tuple from the values and nulls arrays
     */
-   newTuple = heap_formtuple(RelationGetDescr(relation),
-                             value,
-                             nulls);
+   newTuple = heap_formtuple(tupleDesc, values, nulls);
 
-   pfree(value);
+   pfree(values);
    pfree(nulls);
 
    /*
@@ -735,12 +703,96 @@ heap_modifytuple(HeapTuple tuple,
    newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
    newTuple->t_self = tuple->t_self;
    newTuple->t_tableOid = tuple->t_tableOid;
-   if (relation->rd_rel->relhasoids)
+   if (tupleDesc->tdhasoid)
        HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
 
    return newTuple;
 }
 
+/* ----------------
+ *     heap_deformtuple
+ *
+ *     Given a tuple, extract data into values/nulls arrays; this is
+ *     the inverse of heap_formtuple.
+ *
+ *     Storage for the values/nulls arrays is provided by the caller;
+ *     it should be sized according to tupleDesc->natts not tuple->t_natts.
+ *
+ *     Note that for pass-by-reference datatypes, the pointer placed
+ *     in the Datum will point into the given tuple.
+ *
+ *     When all or most of a tuple's fields need to be extracted,
+ *     this routine will be significantly quicker than a loop around
+ *     heap_getattr; the loop will become O(N^2) as soon as any
+ *     noncacheable attribute offsets are involved.
+ * ----------------
+ */
+void
+heap_deformtuple(HeapTuple tuple,
+                TupleDesc tupleDesc,
+                Datum *values,
+                char *nulls)
+{
+   HeapTupleHeader tup = tuple->t_data;
+   Form_pg_attribute *att = tupleDesc->attrs;
+   int         tdesc_natts = tupleDesc->natts;
+   int         natts;          /* number of atts to extract */
+   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 = false;   /* can we use/set attcacheoff? */
+
+   natts = tup->t_natts;
+   /* This min() operation is pure paranoia */
+   natts = Min(natts, tdesc_natts);
+
+   tp = (char *) tup + tup->t_hoff;
+
+   off = 0;
+
+   for (attnum = 0; attnum < natts; attnum++)
+   {
+       if (HeapTupleHasNulls(tuple) && att_isnull(attnum, bp))
+       {
+           values[attnum] = (Datum) 0;
+           nulls[attnum] = 'n';
+           slow = true;        /* can't use attcacheoff anymore */
+           continue;
+       }
+
+       nulls[attnum] = ' ';
+
+       if (!slow && att[attnum]->attcacheoff >= 0)
+       {
+           off = att[attnum]->attcacheoff;
+       }
+       else
+       {
+           off = att_align(off, att[attnum]->attalign);
+
+           if (!slow)
+               att[attnum]->attcacheoff = off;
+       }
+
+       values[attnum] = fetchatt(att[attnum], tp + off);
+
+       off = att_addlength(off, att[attnum]->attlen, tp + off);
+
+       if (att[attnum]->attlen <= 0)
+           slow = true;        /* can't use attcacheoff anymore */
+   }
+
+   /*
+    * If tuple doesn't have all the atts indicated by tupleDesc, read
+    * the rest as null
+    */
+   for (; attnum < tdesc_natts; attnum++)
+   {
+       values[attnum] = (Datum) 0;
+       nulls[attnum] = 'n';
+   }
+}
 
 /* ----------------
  *     heap_freetuple
index eb05c994b465784e43a478f9cf7dcce5c927cd76..fc0b8d9e7ace99cde4834cbac01b40e97a99079e 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.81 2004/05/26 04:41:03 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.82 2004/06/04 20:35:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -65,6 +65,8 @@ typedef struct
    TupleDesc   attrinfo;       /* The attr info we are set up for */
    int         nattrs;
    PrinttupAttrInfo *myinfo;   /* Cached info about each attr */
+   Datum      *values;         /* preallocated space for deformtuple */
+   char       *nulls;
 } DR_printtup;
 
 /* ----------------
@@ -103,6 +105,8 @@ printtup_create_DR(CommandDest dest, Portal portal)
    self->attrinfo = NULL;
    self->nattrs = 0;
    self->myinfo = NULL;
+   self->values = NULL;
+   self->nulls = NULL;
 
    return (DestReceiver *) self;
 }
@@ -243,15 +247,27 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
    int16      *formats = myState->portal->formats;
    int         i;
 
+   /* get rid of any old data */
    if (myState->myinfo)
-       pfree(myState->myinfo); /* get rid of any old data */
+       pfree(myState->myinfo);
    myState->myinfo = NULL;
+   if (myState->values)
+       pfree(myState->values);
+   myState->values = NULL;
+   if (myState->nulls)
+       pfree(myState->nulls);
+   myState->nulls = NULL;
+
    myState->attrinfo = typeinfo;
    myState->nattrs = numAttrs;
    if (numAttrs <= 0)
        return;
+
    myState->myinfo = (PrinttupAttrInfo *)
        palloc0(numAttrs * sizeof(PrinttupAttrInfo));
+   myState->values = (Datum *) palloc(numAttrs * sizeof(Datum));
+   myState->nulls = (char *) palloc(numAttrs * sizeof(char));
+
    for (i = 0; i < numAttrs; i++)
    {
        PrinttupAttrInfo *thisState = myState->myinfo + i;
@@ -297,6 +313,11 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
    if (myState->attrinfo != typeinfo || myState->nattrs != natts)
        printtup_prepare_info(myState, typeinfo, natts);
 
+   /*
+    * deconstruct the tuple (faster than a heap_getattr loop)
+    */
+   heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
+
    /*
     * Prepare a DataRow message
     */
@@ -310,12 +331,10 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
    for (i = 0; i < natts; ++i)
    {
        PrinttupAttrInfo *thisState = myState->myinfo + i;
-       Datum       origattr,
+       Datum       origattr = myState->values[i],
                    attr;
-       bool        isnull;
 
-       origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
-       if (isnull)
+       if (myState->nulls[i] == 'n')
        {
            pq_sendint(&buf, -1, 4);
            continue;
@@ -383,6 +402,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
    if (myState->attrinfo != typeinfo || myState->nattrs != natts)
        printtup_prepare_info(myState, typeinfo, natts);
 
+   /*
+    * deconstruct the tuple (faster than a heap_getattr loop)
+    */
+   heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
+
    /*
     * tell the frontend to expect new tuple data (in ASCII style)
     */
@@ -395,7 +419,7 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
    k = 1 << 7;
    for (i = 0; i < natts; ++i)
    {
-       if (!heap_attisnull(tuple, i + 1))
+       if (myState->nulls[i] != 'n')
            j |= k;             /* set bit if not null */
        k >>= 1;
        if (k == 0)             /* end of byte? */
@@ -414,13 +438,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
    for (i = 0; i < natts; ++i)
    {
        PrinttupAttrInfo *thisState = myState->myinfo + i;
-       Datum       origattr,
+       Datum       origattr = myState->values[i],
                    attr;
-       bool        isnull;
        char       *outputstr;
 
-       origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
-       if (isnull)
+       if (myState->nulls[i] == 'n')
            continue;
 
        Assert(thisState->format == 0);
@@ -461,6 +483,13 @@ printtup_shutdown(DestReceiver *self)
    if (myState->myinfo)
        pfree(myState->myinfo);
    myState->myinfo = NULL;
+   if (myState->values)
+       pfree(myState->values);
+   myState->values = NULL;
+   if (myState->nulls)
+       pfree(myState->nulls);
+   myState->nulls = NULL;
+
    myState->attrinfo = NULL;
 }
 
@@ -587,6 +616,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
    if (myState->attrinfo != typeinfo || myState->nattrs != natts)
        printtup_prepare_info(myState, typeinfo, natts);
 
+   /*
+    * deconstruct the tuple (faster than a heap_getattr loop)
+    */
+   heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
+
    /*
     * tell the frontend to expect new tuple data (in binary style)
     */
@@ -599,7 +633,7 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
    k = 1 << 7;
    for (i = 0; i < natts; ++i)
    {
-       if (!heap_attisnull(tuple, i + 1))
+       if (myState->nulls[i] != 'n')
            j |= k;             /* set bit if not null */
        k >>= 1;
        if (k == 0)             /* end of byte? */
@@ -618,13 +652,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
    for (i = 0; i < natts; ++i)
    {
        PrinttupAttrInfo *thisState = myState->myinfo + i;
-       Datum       origattr,
+       Datum       origattr = myState->values[i],
                    attr;
-       bool        isnull;
        bytea      *outputbytes;
 
-       origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
-       if (isnull)
+       if (myState->nulls[i] == 'n')
            continue;
 
        Assert(thisState->format == 1);
index ae4b2c0aa888c07db76e719e9f28c4dee178ca3e..f206a3f28eb5475abad83767dca9341739f41c3c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.41 2003/11/29 19:51:40 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.42 2004/06/04 20:35:21 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -281,15 +281,26 @@ toast_delete(Relation rel, HeapTuple oldtup)
    Form_pg_attribute *att;
    int         numAttrs;
    int         i;
-   Datum       value;
-   bool        isnull;
+   Datum       toast_values[MaxHeapAttributeNumber];
+   char        toast_nulls[MaxHeapAttributeNumber];
 
    /*
-    * Get the tuple descriptor, the number of and attribute descriptors.
+    * Get the tuple descriptor and break down the tuple into fields.
+    *
+    * NOTE: it's debatable whether to use heap_deformtuple() here or
+    * just heap_getattr() only the varlena columns.  The latter could
+    * win if there are few varlena columns and many non-varlena ones.
+    * However, heap_deformtuple costs only O(N) while the heap_getattr
+    * way would cost O(N^2) if there are many varlena columns, so it
+    * seems better to err on the side of linear cost.  (We won't even
+    * be here unless there's at least one varlena column, by the way.)
     */
    tupleDesc = rel->rd_att;
-   numAttrs = tupleDesc->natts;
    att = tupleDesc->attrs;
+   numAttrs = tupleDesc->natts;
+
+   Assert(numAttrs <= MaxHeapAttributeNumber);
+   heap_deformtuple(oldtup, tupleDesc, toast_values, toast_nulls);
 
    /*
     * Check for external stored attributes and delete them from the
@@ -299,8 +310,9 @@ toast_delete(Relation rel, HeapTuple oldtup)
    {
        if (att[i]->attlen == -1)
        {
-           value = heap_getattr(oldtup, i + 1, tupleDesc, &isnull);
-           if (!isnull && VARATT_IS_EXTERNAL(value))
+           Datum   value = toast_values[i];
+
+           if (toast_nulls[i] != 'n' && VARATT_IS_EXTERNAL(value))
                toast_delete_datum(rel, value);
        }
    }
@@ -321,8 +333,6 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
    Form_pg_attribute *att;
    int         numAttrs;
    int         i;
-   bool        old_isnull;
-   bool        new_isnull;
 
    bool        need_change = false;
    bool        need_free = false;
@@ -333,18 +343,24 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
 
    char        toast_action[MaxHeapAttributeNumber];
    char        toast_nulls[MaxHeapAttributeNumber];
+   char        toast_oldnulls[MaxHeapAttributeNumber];
    Datum       toast_values[MaxHeapAttributeNumber];
+   Datum       toast_oldvalues[MaxHeapAttributeNumber];
    int32       toast_sizes[MaxHeapAttributeNumber];
    bool        toast_free[MaxHeapAttributeNumber];
    bool        toast_delold[MaxHeapAttributeNumber];
 
    /*
-    * Get the tuple descriptor, the number of and attribute descriptors
-    * and the location of the tuple values.
+    * Get the tuple descriptor and break down the tuple(s) into fields.
     */
    tupleDesc = rel->rd_att;
-   numAttrs = tupleDesc->natts;
    att = tupleDesc->attrs;
+   numAttrs = tupleDesc->natts;
+
+   Assert(numAttrs <= MaxHeapAttributeNumber);
+   heap_deformtuple(newtup, tupleDesc, toast_values, toast_nulls);
+   if (oldtup != NULL)
+       heap_deformtuple(oldtup, tupleDesc, toast_oldvalues, toast_oldnulls);
 
    /* ----------
     * Then collect information about the values given
@@ -353,12 +369,15 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
     *      ' '     default handling
     *      'p'     already processed --- don't touch it
     *      'x'     incompressible, but OK to move off
+    *
+    * NOTE: toast_sizes[i] is only made valid for varlena attributes with
+    *      toast_action[i] different from 'p'.
     * ----------
     */
    memset(toast_action, ' ', numAttrs * sizeof(char));
-   memset(toast_nulls, ' ', numAttrs * sizeof(char));
    memset(toast_free, 0, numAttrs * sizeof(bool));
    memset(toast_delold, 0, numAttrs * sizeof(bool));
+
    for (i = 0; i < numAttrs; i++)
    {
        varattrib  *old_value;
@@ -369,27 +388,24 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
            /*
             * For UPDATE get the old and new values of this attribute
             */
-           old_value = (varattrib *) DatumGetPointer(
-                   heap_getattr(oldtup, i + 1, tupleDesc, &old_isnull));
-           toast_values[i] =
-               heap_getattr(newtup, i + 1, tupleDesc, &new_isnull);
+           old_value = (varattrib *) DatumGetPointer(toast_oldvalues[i]);
            new_value = (varattrib *) DatumGetPointer(toast_values[i]);
 
            /*
             * If the old value is an external stored one, check if it has
             * changed so we have to delete it later.
             */
-           if (!old_isnull && att[i]->attlen == -1 &&
+           if (att[i]->attlen == -1 && toast_oldnulls[i] != 'n' &&
                VARATT_IS_EXTERNAL(old_value))
            {
-               if (new_isnull || !VARATT_IS_EXTERNAL(new_value) ||
+               if (toast_nulls[i] == 'n' || !VARATT_IS_EXTERNAL(new_value) ||
                    old_value->va_content.va_external.va_valueid !=
                    new_value->va_content.va_external.va_valueid ||
                    old_value->va_content.va_external.va_toastrelid !=
                    new_value->va_content.va_external.va_toastrelid)
                {
                    /*
-                    * The old external store value isn't needed any more
+                    * The old external stored value isn't needed any more
                     * after the update
                     */
                    toast_delold[i] = true;
@@ -413,23 +429,21 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
            /*
             * For INSERT simply get the new value
             */
-           toast_values[i] =
-               heap_getattr(newtup, i + 1, tupleDesc, &new_isnull);
+           new_value = (varattrib *) DatumGetPointer(toast_values[i]);
        }
 
        /*
         * Handle NULL attributes
         */
-       if (new_isnull)
+       if (toast_nulls[i] == 'n')
        {
            toast_action[i] = 'p';
-           toast_nulls[i] = 'n';
            has_nulls = true;
            continue;
        }
 
        /*
-        * Now look at varsize attributes
+        * Now look at varlena attributes
         */
        if (att[i]->attlen == -1)
        {
@@ -461,10 +475,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
        else
        {
            /*
-            * Not a variable size attribute, plain storage always
+            * Not a varlena attribute, plain storage always
             */
            toast_action[i] = 'p';
-           toast_sizes[i] = att[i]->attlen;
        }
    }
 
@@ -768,8 +781,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
    if (need_delold)
        for (i = 0; i < numAttrs; i++)
            if (toast_delold[i])
-               toast_delete_datum(rel,
-                   heap_getattr(oldtup, i + 1, tupleDesc, &old_isnull));
+               toast_delete_datum(rel, toast_oldvalues[i]);
 }
 
 
index 71f5ad0c00173143228e11ece7e27f0f0ed5f0fc..492532363a579c99f2d3eefeaced2a9db9d0bd82 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.109 2004/06/02 21:01:08 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.110 2004/06/04 20:35:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2342,10 +2342,10 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
        newslot = MakeTupleTableSlot();
        ExecSetSlotDescriptor(newslot, newTupDesc, false);
 
-       /* Preallocate values/nulls arrays (+1 in case natts==0) */
+       /* Preallocate values/nulls arrays */
        i = Max(newTupDesc->natts, oldTupDesc->natts);
-       values = (Datum *) palloc(i * sizeof(Datum) + 1);
-       nulls = (char *) palloc(i * sizeof(char) + 1);
+       values = (Datum *) palloc(i * sizeof(Datum));
+       nulls = (char *) palloc(i * sizeof(char));
        memset(values, 0, i * sizeof(Datum));
        memset(nulls, 'n', i * sizeof(char));
 
@@ -2363,24 +2363,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
                 * Extract data from old tuple.  We can force to null any
                 * columns that are deleted according to the new tuple.
                 */
-               int     natts = oldTupDesc->natts;
-               bool    isNull;
+               int     natts = newTupDesc->natts;
+
+               heap_deformtuple(tuple, oldTupDesc, values, nulls);
 
                for (i = 0; i < natts; i++)
                {
                    if (newTupDesc->attrs[i]->attisdropped)
                        nulls[i] = 'n';
-                   else
-                   {
-                       values[i] = heap_getattr(tuple,
-                                                i + 1,
-                                                oldTupDesc,
-                                                &isNull);
-                       if (isNull)
-                           nulls[i] = 'n';
-                       else
-                           nulls[i] = ' ';
-                   }
                }
 
                /*
@@ -2393,6 +2383,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
                foreach(l, tab->newvals)
                {
                    NewColumnValue   *ex = lfirst(l);
+                   bool    isNull;
 
                    values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
                                                          econtext,
index d8a2a5b20f5d91c7dfc32d48ae7edc8c80357857..439ad91cc37e7d91eb9fe56cfcfc7c998f06c825 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.57 2004/05/26 04:41:12 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.58 2004/06/04 20:35:21 tgl Exp $
  *
  * DESCRIPTION
  *   The "DefineFoo" routines take the parse tree and pick out the
@@ -1329,12 +1329,8 @@ AlterDomainNotNull(List *names, bool notNull)
                for (i = 0; i < rtc->natts; i++)
                {
                    int         attnum = rtc->atts[i];
-                   Datum       d;
-                   bool        isNull;
 
-                   d = heap_getattr(tuple, attnum, tupdesc, &isNull);
-
-                   if (isNull)
+                   if (heap_attisnull(tuple, attnum))
                        ereport(ERROR,
                                (errcode(ERRCODE_NOT_NULL_VIOLATION),
                                 errmsg("column \"%s\" of table \"%s\" contains null values",
index 20c385134b272d95ec8c3afb91c2d1e8d6cef512..8c693ddc98c7062b889122c1891b8254ab334c7f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.40 2004/05/26 04:41:14 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.41 2004/06/04 20:35:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -246,12 +246,15 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
    TupleDesc   cleanTupType;
    TupleDesc   tupType;
    int         cleanLength;
-   bool        isNull;
    int         i;
    Datum      *values;
    char       *nulls;
+   Datum      *old_values;
+   char       *old_nulls;
    Datum       values_array[64];
+   Datum       old_values_array[64];
    char        nulls_array[64];
+   char        old_nulls_array[64];
 
    /*
     * get info from the slot and the junk filter
@@ -265,11 +268,15 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
 
    /*
     * Create the arrays that will hold the attribute values and the null
-    * information for the new "clean" tuple.
+    * information for the old tuple and new "clean" tuple.
     *
     * Note: we use memory on the stack to optimize things when we are
     * dealing with a small number of attributes. for large tuples we just
     * use palloc.
+    *
+    * Note: we could use just one set of arrays if we were willing to
+    * assume that the resno mapping is monotonic... I think it is, but
+    * won't take the risk of breaking things right now.
     */
    if (cleanLength > 64)
    {
@@ -281,36 +288,52 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
        values = values_array;
        nulls = nulls_array;
    }
+   if (tupType->natts > 64)
+   {
+       old_values = (Datum *) palloc(tupType->natts * sizeof(Datum));
+       old_nulls = (char *) palloc(tupType->natts * sizeof(char));
+   }
+   else
+   {
+       old_values = old_values_array;
+       old_nulls = old_nulls_array;
+   }
 
    /*
-    * Exctract one by one all the values of the "clean" tuple.
+    * Extract all the values of the old tuple.
+    */
+   heap_deformtuple(tuple, tupType, old_values, old_nulls);
+
+   /*
+    * Transpose into proper fields of the new tuple.
     */
    for (i = 0; i < cleanLength; i++)
    {
-       values[i] = heap_getattr(tuple, cleanMap[i], tupType, &isNull);
+       int j = cleanMap[i] - 1;
 
-       if (isNull)
-           nulls[i] = 'n';
-       else
-           nulls[i] = ' ';
+       values[i] = old_values[j];
+       nulls[i] = old_nulls[j];
    }
 
    /*
     * Now form the new tuple.
     */
-   cleanTuple = heap_formtuple(cleanTupType,
-                               values,
-                               nulls);
+   cleanTuple = heap_formtuple(cleanTupType, values, nulls);
 
    /*
     * We are done.  Free any space allocated for 'values' and 'nulls' and
     * return the new tuple.
     */
-   if (cleanLength > 64)
+   if (values != values_array)
    {
        pfree(values);
        pfree(nulls);
    }
+   if (old_values != old_values_array)
+   {
+       pfree(old_values);
+       pfree(old_nulls);
+   }
 
    return cleanTuple;
 }
index 6ec4d810a8e3c0cfcde9d694636b560a60dbfda4..047b8fa2aae66d687427cf97e14283543c5e09d3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.115 2004/05/30 23:40:26 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.116 2004/06/04 20:35:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -427,7 +427,6 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
    int         numberOfAttributes;
    Datum      *v;
    char       *n;
-   bool        isnull;
    int         i;
 
    if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
@@ -448,11 +447,7 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
    n = (char *) palloc(numberOfAttributes * sizeof(char));
 
    /* fetch old values and nulls */
-   for (i = 0; i < numberOfAttributes; i++)
-   {
-       v[i] = heap_getattr(tuple, i + 1, rel->rd_att, &isnull);
-       n[i] = (isnull) ? 'n' : ' ';
-   }
+   heap_deformtuple(tuple, rel->rd_att, v, n);
 
    /* replace values and nulls */
    for (i = 0; i < natts; i++)
@@ -474,7 +469,7 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
        mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
        mtuple->t_self = tuple->t_self;
        mtuple->t_tableOid = tuple->t_tableOid;
-       if (rel->rd_rel->relhasoids)
+       if (rel->rd_att->tdhasoid)
            HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
    }
    else
index 9f138524155560fd4190eea1c4ce90230b1c8879..5fa50d28e1e8b69bd232fdf96b71cde36edc2656 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.89 2004/04/21 18:24:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.90 2004/06/04 20:35:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -184,9 +184,9 @@ extern XLogRecPtr log_heap_move(Relation reln, Buffer oldbuf,
              Buffer newbuf, HeapTuple newtup);
 
 /* in common/heaptuple.c */
-extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *value, char *nulls);
+extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *values, char *nulls);
 extern void DataFill(char *data, TupleDesc tupleDesc,
-        Datum *value, char *nulls, uint16 *infomask,
+        Datum *values, char *nulls, uint16 *infomask,
         bits8 *bit);
 extern int heap_attisnull(HeapTuple tup, int attnum);
 extern Datum nocachegetattr(HeapTuple tup, int attnum,
@@ -194,9 +194,14 @@ extern Datum nocachegetattr(HeapTuple tup, int attnum,
 extern HeapTuple heap_copytuple(HeapTuple tuple);
 extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
 extern HeapTuple heap_formtuple(TupleDesc tupleDescriptor,
-              Datum *value, char *nulls);
+              Datum *values, char *nulls);
 extern HeapTuple heap_modifytuple(HeapTuple tuple,
-       Relation relation, Datum *replValue, char *replNull, char *repl);
+                                 Relation relation,
+                                 Datum *replValues,
+                                 char *replNulls,
+                                 char *replActions);
+extern void heap_deformtuple(HeapTuple tuple, TupleDesc tupleDesc,
+                            Datum *values, char *nulls);
 extern void heap_freetuple(HeapTuple tuple);
 extern HeapTuple heap_addheader(int natts, bool withoid, Size structlen, void *structure);