]> granicus.if.org Git - postgresql/commitdiff
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);