]> granicus.if.org Git - postgresql/commitdiff
Fix tuptoaster bugs induced by making bytea toastable. Durn thing was
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Aug 2000 04:16:17 +0000 (04:16 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Aug 2000 04:16:17 +0000 (04:16 +0000)
trying to toast tuples inserted into toast tables!  Fix is two-pronged:
first, ensure all columns of a toast table are marked attstorage='p',
and second, alter the target chunk size so that it's less than the
threshold for trying to toast a tuple.  (Code tried to do that but the
expression was wrong.)  A few cosmetic cleanups in tuptoaster too.
NOTE: initdb forced due to change in toaster chunk-size.

src/backend/access/heap/heapam.c
src/backend/access/heap/tuptoaster.c
src/backend/commands/command.c
src/include/access/tuptoaster.h
src/include/catalog/catversion.h

index 1e22c0304620ef9dc873c555e61b36eed34a5708..d5f36e4e4d9468db715b0231209f82b50eb71473 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.83 2000/08/03 19:18:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.84 2000/08/04 04:16:06 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1362,7 +1362,7 @@ heap_insert(Relation relation, HeapTuple tup)
         * ----------
         */
     if (HeapTupleHasExtended(tup) ||
-                               (MAXALIGN(tup->t_len) > (MaxTupleSize / 4)))
+               (MAXALIGN(tup->t_len) > TOAST_TUPLE_THRESHOLD))
                heap_tuple_toast_attrs(relation, tup, NULL);
 #endif
 
@@ -1621,13 +1621,15 @@ l2:
 #ifdef TUPLE_TOASTER_ACTIVE
        /* ----------
         * If this relation is enabled for toasting, let the toaster
-        * delete not any longer needed entries and create new ones to
-        * make the new tuple fit again.
+        * delete any no-longer-needed entries and create new ones to
+        * make the new tuple fit again.  Also, if there are already-
+        * toasted values from some other relation, the toaster must
+        * fix them.
         * ----------
         */
        if (HeapTupleHasExtended(&oldtup) || 
-                       HeapTupleHasExtended(newtup) ||
-                       (MAXALIGN(newtup->t_len) > (MaxTupleSize / 4)))
+               HeapTupleHasExtended(newtup) ||
+               (MAXALIGN(newtup->t_len) > TOAST_TUPLE_THRESHOLD))
                heap_tuple_toast_attrs(relation, newtup, &oldtup);
 #endif
 
index 61076444a2df525b9fb0a10e0209c0d1f07a1047..05952bc80e87c10899d301a063124adc697da0b4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.11 2000/08/03 16:33:40 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.12 2000/08/04 04:16:07 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
  *-------------------------------------------------------------------------
  */
 
-#include <stdio.h>
-#include <stdlib.h>
+#include "postgres.h"
+
 #include <unistd.h>
 #include <fcntl.h>
-#include "postgres.h"
 
 #include "access/heapam.h"
 #include "access/genam.h"
@@ -39,6 +38,7 @@
 
 
 #ifdef TUPLE_TOASTER_ACTIVE
+
 #undef TOAST_DEBUG
 
 static void                    toast_delete(Relation rel, HeapTuple oldtup);
@@ -47,7 +47,6 @@ static void                   toast_insert_or_update(Relation rel, HeapTuple newtup,
                                                                HeapTuple oldtup);
 static Datum           toast_compress_datum(Datum value);
 static Datum           toast_save_datum(Relation rel, Oid mainoid, int16 attno, Datum value);
-                                                               
 static varattrib   *toast_fetch_datum(varattrib *attr);
 
 
@@ -209,7 +208,7 @@ toast_delete(Relation rel, HeapTuple oldtup)
 /* ----------
  * toast_insert_or_update -
  *
- *     Delete no more used toast-entries and create new ones to
+ *     Delete no-longer-used toast-entries and create new ones to
  *     make the new tuple fit on INSERT or UPDATE
  * ----------
  */
@@ -375,7 +374,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
        }
 
        /* ----------
-        * Compress and/or save external until data fits
+        * Compress and/or save external until data fits into target length
         *
         *      1: Inline compress attributes with attstorage 'x'
         *      2: Store attributes with attstorage 'x' or 'e' external
@@ -386,7 +385,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
        maxDataLen = offsetof(HeapTupleHeaderData, t_bits);
        if (has_nulls)
                maxDataLen += BITMAPLEN(numAttrs);
-       maxDataLen = (MaxTupleSize / 4) - MAXALIGN(maxDataLen);
+       maxDataLen = TOAST_TUPLE_TARGET - MAXALIGN(maxDataLen);
 
        /* ----------
         * Look for attributes with attstorage 'x' to compress
@@ -560,7 +559,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
 
                /* ----------
                 * Search for the biggest yet inlined attribute with
-                * attstorage = 'x' or 'e'
+                * attstorage = 'm'
                 * ----------
                 */
                for (i = 0; i < numAttrs; i++)
@@ -684,15 +683,13 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
                        if (toast_delold[i])
                                toast_delete_datum(rel,
                                        heap_getattr(oldtup, i + 1, tupleDesc, &old_isnull));
-
-       return;
 }
 
 
 /* ----------
  * toast_compress_datum -
  *
- *     Create a compressed version of a datum
+ *     Create a compressed version of a varlena datum
  * ----------
  */
 static Datum
@@ -726,9 +723,9 @@ toast_save_datum(Relation rel, Oid mainoid, int16 attno, Datum value)
        InsertIndexResult       idxres;
        TupleDesc                       toasttupDesc;
        Datum                           t_values[3];
-       char                            t_nulls[4];
+       char                            t_nulls[3];
        varattrib                  *result;
-       char                            chunk_data[MaxTupleSize];
+       char                            chunk_data[VARHDRSZ + TOAST_MAX_CHUNK_SIZE];
        int32                           chunk_size;
        int32                           chunk_seq = 0;
        char                       *data_p;
@@ -769,7 +766,6 @@ toast_save_datum(Relation rel, Oid mainoid, int16 attno, Datum value)
        t_nulls[0] = ' ';
        t_nulls[1] = ' ';
        t_nulls[2] = ' ';
-       t_nulls[3] = '\0';
 
        /* ----------
         * Get the data to process
@@ -783,16 +779,8 @@ toast_save_datum(Relation rel, Oid mainoid, int16 attno, Datum value)
         * ----------
         */
        toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock);
-       if (toastrel == NULL)
-               elog(ERROR, "Failed to open secondary relation of %s",
-                                       DatumGetCString(DirectFunctionCall1(nameout,
-                                       NameGetDatum(&(rel->rd_rel->relname)))));
        toasttupDesc = toastrel->rd_att;
        toastidx = index_open(rel->rd_rel->reltoastidxid);
-       if (toastidx == NULL)
-               elog(ERROR, "Failed to open index for secondary relation of %s",
-                                       DatumGetCString(DirectFunctionCall1(nameout,
-                                       NameGetDatum(&(rel->rd_rel->relname)))));
        
        /* ----------
         * Split up the item into chunks 
@@ -804,14 +792,13 @@ toast_save_datum(Relation rel, Oid mainoid, int16 attno, Datum value)
                 * Calculate the size of this chunk
                 * ----------
                 */
-               chunk_size = (TOAST_MAX_CHUNK_SIZE < data_todo) ? 
-                                               TOAST_MAX_CHUNK_SIZE : data_todo;
+               chunk_size = Min(TOAST_MAX_CHUNK_SIZE, data_todo);
 
                /* ----------
                 * Build a tuple
                 * ----------
                 */
-               t_values[1] = (Datum)(chunk_seq++);
+               t_values[1] = Int32GetDatum(chunk_seq++);
                VARATT_SIZEP(chunk_data) = chunk_size + VARHDRSZ;
                memcpy(VARATT_DATA(chunk_data), data_p, chunk_size);
                toasttup = heap_formtuple(toasttupDesc, t_values, t_nulls);
@@ -882,11 +869,7 @@ toast_delete_datum(Relation rel, Datum value)
         */
        toastrel        = heap_open(attr->va_content.va_external.va_toastrelid,
                                        RowExclusiveLock);
-       if (toastrel == NULL)
-               elog(ERROR, "Failed to open secondary relation at TOAST fetch");
        toastidx = index_open(attr->va_content.va_external.va_toastidxid);
-       if (toastidx == NULL)
-               elog(ERROR, "Failed to open index of secondary relation at TOAST fetch");
 
        /* ----------
         * Setup a scan key to fetch from the index by va_valueid
@@ -928,8 +911,6 @@ toast_delete_datum(Relation rel, Datum value)
        index_endscan(toastscan);
        index_close(toastidx);
        heap_close(toastrel, RowExclusiveLock);
-
-       return;
 }
 
 
@@ -957,14 +938,15 @@ toast_fetch_datum(varattrib *attr)
        int32                                   ressize;
        int32                                   residx;
        int                                             numchunks;
-       Datum                                   chunk;
+       Pointer                                 chunk;
        bool                                    isnull;
+       int32                                   chunksize;
 
        char                               *chunks_found;
        char                               *chunks_expected;
 
        ressize = attr->va_content.va_external.va_extsize;
-    numchunks = (ressize / TOAST_MAX_CHUNK_SIZE) + 1;
+    numchunks = ((ressize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
 
        chunks_found    = palloc(numchunks);
        chunks_expected = palloc(numchunks);
@@ -982,12 +964,8 @@ toast_fetch_datum(varattrib *attr)
         */
        toastrel        = heap_open(attr->va_content.va_external.va_toastrelid,
                                        AccessShareLock);
-       if (toastrel == NULL)
-               elog(ERROR, "Failed to open secondary relation at TOAST fetch");
        toasttupDesc = toastrel->rd_att;
        toastidx = index_open(attr->va_content.va_external.va_toastidxid);
-       if (toastidx == NULL)
-               elog(ERROR, "Failed to open index of secondary relation at TOAST fetch");
 
        /* ----------
         * Setup a scan key to fetch from the index by va_valueid
@@ -1001,6 +979,8 @@ toast_fetch_datum(varattrib *attr)
 
        /* ----------
         * Read the chunks by index
+        *
+        * Note we will not necessarily see the chunks in sequence-number order.
         * ----------
         */
        toastscan = index_beginscan(toastidx, false, 1, &toastkey);
@@ -1018,30 +998,46 @@ toast_fetch_datum(varattrib *attr)
                 * Have a chunk, extract the sequence number and the data
                 * ----------
                 */
-               residx = (int32)heap_getattr(ttup, 2, toasttupDesc, &isnull);
-               chunk = heap_getattr(ttup, 3, toasttupDesc, &isnull);
+               residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull));
+               Assert(!isnull);
+               chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull));
+               Assert(!isnull);
+               chunksize = VARATT_SIZE(chunk) - VARHDRSZ;
 
                /* ----------
                 * Some checks on the data we've found
                 * ----------
                 */
-               if (residx * TOAST_MAX_CHUNK_SIZE + VARATT_SIZE(chunk) - VARHDRSZ
-                                               > ressize)
-                       elog(ERROR, "chunk data exceeds original data size for "
-                                               "toast value %d", 
-                                               attr->va_content.va_external.va_valueid);
+               if (residx < 0 || residx >= numchunks)
+                       elog(ERROR, "unexpected chunk number %d for toast value %d",
+                                residx,
+                                attr->va_content.va_external.va_valueid);
+               if (residx < numchunks-1)
+               {
+                       if (chunksize != TOAST_MAX_CHUNK_SIZE)
+                               elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %d",
+                                        chunksize, residx,
+                                        attr->va_content.va_external.va_valueid);
+               }
+               else
+               {
+                       if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != ressize)
+                               elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %d",
+                                        chunksize, residx,
+                                        attr->va_content.va_external.va_valueid);
+               }
                if (chunks_found[residx]++ > 0)
                        elog(ERROR, "chunk %d for toast value %d appears multiple times",
-                                               residx,
-                                               attr->va_content.va_external.va_valueid);
+                                residx,
+                                attr->va_content.va_external.va_valueid);
 
                /* ----------
-                * Copy the data into our result
+                * Copy the data into proper place in our result
                 * ----------
                 */
                memcpy(((char *)VARATT_DATA(result)) + residx * TOAST_MAX_CHUNK_SIZE,
-                                       VARATT_DATA(chunk),
-                                       VARATT_SIZE(chunk) - VARHDRSZ);
+                          VARATT_DATA(chunk),
+                          chunksize);
 
                ReleaseBuffer(buffer);
        }
index de2597bd266016ea9a3b58aaf0c39605fcf8f5f0..68487f64ffce08b5e61edfa2629fcaefeb3f75d9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.93 2000/08/03 19:19:18 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.94 2000/08/04 04:16:06 tgl Exp $
  *
  * NOTES
  *       The PerformAddAttribute() code, like most of the relation
@@ -1503,6 +1503,14 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
                                           "chunk_data",
                                           BYTEAOID,
                                           -1, 0, false);
+       /*
+        * Ensure that the toast table doesn't itself get toasted,
+        * or we'll be toast :-(.  This is essential for chunk_data because
+        * type bytea is toastable; hit the other two just to be sure.
+        */
+       tupdesc->attrs[0]->attstorage = 'p';
+       tupdesc->attrs[1]->attstorage = 'p';
+       tupdesc->attrs[2]->attstorage = 'p';
 
        /*
         * Note: the toast relation is considered a "normal" relation even if
index 91149f5f8362082e91bf87d1bc2fb65cb6b60060..23f31aec522572def9504b8c91623880d3d088bd 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 2000, PostgreSQL Development Team
  *
- * $Id: tuptoaster.h,v 1.7 2000/07/22 11:18:47 wieck Exp $
+ * $Id: tuptoaster.h,v 1.8 2000/08/04 04:16:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "access/tupmacs.h"
 #include "utils/rel.h"
 
+
+/*
+ * This enables de-toasting of index entries.  Needed until VACUUM is
+ * smart enough to rebuild indexes from scratch.
+ */
 #define TOAST_INDEX_HACK
 
 
-#define        TOAST_MAX_CHUNK_SIZE    ((MaxTupleSize -                                                        \
-                               MAXALIGN(                                                                                               \
-                                       MAXALIGN(offsetof(HeapTupleHeaderData, t_bits)) +       \
-                                       MAXALIGN(sizeof(Oid)) +                                                         \
-                                       MAXALIGN(sizeof(int32)) +                                                       \
-                                       MAXALIGN(VARHDRSZ))) / 4)
+/*
+ * These symbols control toaster activation.  If a tuple is larger than
+ * TOAST_TUPLE_THRESHOLD, we will try to toast it down to no more than
+ * TOAST_TUPLE_TARGET bytes.  Both numbers include all tuple header and
+ * alignment-padding overhead.
+ *
+ * The numbers need not be the same, though they currently are.
+ */
+#define TOAST_TUPLE_THRESHOLD  (MaxTupleSize / 4)
+
+#define TOAST_TUPLE_TARGET             (MaxTupleSize / 4)
+
+
+/*
+ * When we store an oversize datum externally, we divide it into chunks
+ * containing at most TOAST_MAX_CHUNK_SIZE data bytes.  This number *must*
+ * be small enough that the completed toast-table tuple (including the
+ * ID and sequence fields and all overhead) is no more than MaxTupleSize
+ * bytes.  It *should* be small enough to make toast-table tuples no more
+ * than TOAST_TUPLE_THRESHOLD bytes, else heapam.c will uselessly invoke
+ * the toaster on toast-table tuples.
+ *
+ * NB: you cannot change this value without forcing initdb, at least not
+ * if your DB contains any multi-chunk toasted values.
+ */
+#define TOAST_MAX_CHUNK_SIZE   (TOAST_TUPLE_THRESHOLD -                        \
+                       MAXALIGN(                                                                                               \
+                               MAXALIGN(offsetof(HeapTupleHeaderData, t_bits)) +       \
+                               sizeof(Oid) +                                                                           \
+                               sizeof(int32) +                                                                         \
+                               VARHDRSZ))
 
 
 /* ----------
  * heap_tuple_toast_attrs() -
  *
  *             Called by heap_insert(), heap_update() and heap_delete().
- *             Outdates not any longer needed toast entries referenced
- *             by oldtup and creates new ones until newtup is smaller
- *             that ~2K (or running out of toastable values).
+ *             Outdates any no-longer-needed toast entries referenced
+ *             by oldtup and creates new ones until newtup is no more than
+ *             TOAST_TUPLE_TARGET (or we run out of toastable values).
  *             Possibly modifies newtup by replacing the t_data part!
+ *
+ *             oldtup is NULL if insert, newtup is NULL if delete.
  * ----------
  */
 extern void heap_tuple_toast_attrs(Relation rel,
index 0a8b0d43ccef8e57b9c724c5b4a233f87f04b74a..c9ac2c39abae763497dfb1fb8bf8047084232150 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.38 2000/07/30 22:13:59 tgl Exp $
+ * $Id: catversion.h,v 1.39 2000/08/04 04:16:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200007301
+#define CATALOG_VERSION_NO     200008031
 
 #endif