Make sure that GIN fast-insert and regular code paths enforce the same
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Oct 2009 21:14:04 +0000 (21:14 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Oct 2009 21:14:04 +0000 (21:14 +0000)
tuple size limit.  Improve the error message for index-tuple-too-large
so that it includes the actual size, the limit, and the index name.
Sync with the btree occurrences of the same error.

Back-patch to 8.4 because it appears that the out-of-sync problem
is occurring in the field.

Teodor and Tom

src/backend/access/gin/ginentrypage.c
src/backend/access/gin/ginfast.c
src/backend/access/gin/gininsert.c
src/backend/access/gin/ginvacuum.c
src/backend/access/nbtree/nbtinsert.c
src/backend/access/nbtree/nbtsort.c
src/include/access/gin.h

index 868a1e187aa8e3a68c6b675a4b0ef45cd30f1f58..a96860d70c231f2367324ebbe6611e954127c0c1 100644 (file)
 #include "postgres.h"
 
 #include "access/gin.h"
-#include "access/tuptoaster.h"
 #include "storage/bufmgr.h"
 #include "utils/rel.h"
 
 /*
  * Form a tuple for entry tree.
  *
+ * If the tuple would be too big to be stored, function throws a suitable
+ * error if errorTooBig is TRUE, or returns NULL if errorTooBig is FALSE.
+ *
  * On leaf pages, Index tuple has non-traditional layout. Tuple may contain
  * posting list or root blocknumber of posting tree.
  * Macros: GinIsPostingTree(itup) / GinSetPostingTree(itup, blkno)
  * and value.
  */
 IndexTuple
-GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key, ItemPointerData *ipd, uint32 nipd)
+GinFormTuple(Relation index, GinState *ginstate,
+                        OffsetNumber attnum, Datum key,
+                        ItemPointerData *ipd, uint32 nipd, bool errorTooBig)
 {
        bool            isnull[2] = {FALSE, FALSE};
        IndexTuple      itup;
+       uint32          newsize;
 
        if (ginstate->oneCol)
                itup = index_form_tuple(ginstate->origTupdesc, &key, isnull);
@@ -69,13 +74,19 @@ GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key, ItemPointerData
 
        if (nipd > 0)
        {
-               uint32          newsize = MAXALIGN(SHORTALIGN(IndexTupleSize(itup)) + sizeof(ItemPointerData) * nipd);
-
-               if (newsize >= INDEX_SIZE_MASK)
-                       return NULL;
-
-               if (newsize > TOAST_INDEX_TARGET && nipd > 1)
+               newsize = MAXALIGN(SHORTALIGN(IndexTupleSize(itup)) + sizeof(ItemPointerData) * nipd);
+               if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
+               {
+                       if (errorTooBig)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                                errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
+                                                               (unsigned long) newsize,
+                                                               (unsigned long) Min(INDEX_SIZE_MASK,
+                                                                                                       GinMaxItemSize),
+                                                               RelationGetRelationName(index))));
                        return NULL;
+               }
 
                itup = repalloc(itup, newsize);
 
@@ -89,6 +100,29 @@ GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key, ItemPointerData
        }
        else
        {
+               /*
+                * Gin tuple without any ItemPointers should be large enough to keep
+                * one ItemPointer, to prevent inconsistency between
+                * ginHeapTupleFastCollect and ginEntryInsert called by
+                * ginHeapTupleInsert.  ginHeapTupleFastCollect forms tuple without
+                * extra pointer to heap, but ginEntryInsert (called for pending list
+                * cleanup during vacuum) will form the same tuple with one
+                * ItemPointer.
+                */
+               newsize = MAXALIGN(SHORTALIGN(IndexTupleSize(itup)) + sizeof(ItemPointerData));
+               if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
+               {
+                       if (errorTooBig)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                                errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
+                                                               (unsigned long) newsize,
+                                                               (unsigned long) Min(INDEX_SIZE_MASK,
+                                                                                                       GinMaxItemSize),
+                                                               RelationGetRelationName(index))));
+                       return NULL;
+               }
+
                GinSetNPosting(itup, 0);
        }
        return itup;
index ceb65e07bcbd922a11a21c71d953237a9ce691e6..ee4afd8d99fb806ea2d5fabe468fddcaac5277a2 100644 (file)
@@ -20,7 +20,6 @@
 
 #include "access/genam.h"
 #include "access/gin.h"
-#include "access/tuptoaster.h"
 #include "catalog/index.h"
 #include "commands/vacuum.h"
 #include "miscadmin.h"
@@ -465,16 +464,10 @@ ginHeapTupleFastCollect(Relation index, GinState *ginstate,
         */
        for (i = 0; i < nentries; i++)
        {
-               int32           tupsize;
-
-               collector->tuples[collector->ntuples + i] = GinFormTuple(ginstate, attnum, entries[i], NULL, 0);
+               collector->tuples[collector->ntuples + i] =
+                       GinFormTuple(index, ginstate, attnum, entries[i], NULL, 0, true);
                collector->tuples[collector->ntuples + i]->t_tid = *item;
-               tupsize = IndexTupleSize(collector->tuples[collector->ntuples + i]);
-
-               if (tupsize > TOAST_INDEX_TARGET || tupsize >= GinMaxItemSize)
-                       elog(ERROR, "huge tuple");
-
-               collector->sumsize += tupsize;
+               collector->sumsize += IndexTupleSize(collector->tuples[collector->ntuples + i]);
        }
 
        collector->ntuples += nentries;
index 4b4ccae83b906e51e2e68db266affecf7fe3d1bb..23122eace89dd368cb1647171661b699716e1c0e 100644 (file)
@@ -102,8 +102,9 @@ addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
 {
        Datum           key = gin_index_getattr(ginstate, old);
        OffsetNumber attnum = gintuple_get_attrnum(ginstate, old);
-       IndexTuple      res = GinFormTuple(ginstate, attnum, key,
-                                                                  NULL, nitem + GinGetNPosting(old));
+       IndexTuple      res = GinFormTuple(index, ginstate, attnum, key,
+                                                                  NULL, nitem + GinGetNPosting(old),
+                                                                  false);
 
        if (res)
        {
@@ -122,7 +123,7 @@ addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
                GinPostingTreeScan *gdi;
 
                /* posting list becomes big, so we need to make posting's tree */
-               res = GinFormTuple(ginstate, attnum, key, NULL, 0);
+               res = GinFormTuple(index, ginstate, attnum, key, NULL, 0, true);
                postingRoot = createPostingTree(index, GinGetPosting(old), GinGetNPosting(old));
                GinSetPostingTree(res, postingRoot);
 
@@ -185,13 +186,12 @@ ginEntryInsert(Relation index, GinState *ginstate,
        }
        else
        {
-               /* We suppose, that tuple can store at list one itempointer */
-               itup = GinFormTuple(ginstate, attnum, value, items, 1);
-               if (itup == NULL || IndexTupleSize(itup) >= GinMaxItemSize)
-                       elog(ERROR, "huge tuple");
+               /* We suppose that tuple can store at least one itempointer */
+               itup = GinFormTuple(index, ginstate, attnum, value, items, 1, true);
 
                if (nitem > 1)
                {
+                       /* Add the rest, making a posting tree if necessary */
                        IndexTuple      previtup = itup;
 
                        itup = addItemPointersToTuple(index, ginstate, stack, previtup, items + 1, nitem - 1, isBuild);
index e9adf07a9ea896b06e02b76eb53647f07870988f..576f80144577827064a556c0c0091ca96e0e4cfc 100644 (file)
@@ -564,7 +564,8 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
 
                                value = gin_index_getattr(&gvs->ginstate, itup);
                                attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
-                               itup = GinFormTuple(&gvs->ginstate, attnum, value, GinGetPosting(itup), newN);
+                               itup = GinFormTuple(gvs->index, &gvs->ginstate, attnum, value,
+                                                                       GinGetPosting(itup), newN, true);
                                PageIndexTupleDelete(tmppage, i);
 
                                if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false, false) != i)
index caa8928aa017a7e80d6bef5a078709809a2ef123..a983571b28c02111807a957d883ff46a993c8dc0 100644 (file)
@@ -518,9 +518,10 @@ _bt_findinsertloc(Relation rel,
        if (itemsz > BTMaxItemSize(page))
                ereport(ERROR,
                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                                errmsg("index row size %lu exceeds btree maximum, %lu",
+                                errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
                                                (unsigned long) itemsz,
-                                               (unsigned long) BTMaxItemSize(page)),
+                                               (unsigned long) BTMaxItemSize(page),
+                                               RelationGetRelationName(rel)),
                errhint("Values larger than 1/3 of a buffer page cannot be indexed.\n"
                                "Consider a function index of an MD5 hash of the value, "
                                "or use full text indexing.")));
index f97e447bc37615b926e60cafa550d434f37f87b3..00bc86e9a21bdb4428da026469afc9aec761af6e 100644 (file)
@@ -480,9 +480,10 @@ _bt_buildadd(BTWriteState *wstate, BTPageState *state, IndexTuple itup)
        if (itupsz > BTMaxItemSize(npage))
                ereport(ERROR,
                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                                errmsg("index row size %lu exceeds btree maximum, %lu",
+                                errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
                                                (unsigned long) itupsz,
-                                               (unsigned long) BTMaxItemSize(npage)),
+                                               (unsigned long) BTMaxItemSize(npage),
+                                               RelationGetRelationName(wstate->index)),
                errhint("Values larger than 1/3 of a buffer page cannot be indexed.\n"
                                "Consider a function index of an MD5 hash of the value, "
                                "or use full text indexing.")));
index 8ce397ea6a62f0ce5e3d428355819b5511d38fff..62cd9b696902457ee5675cd8bf37a32fc9d209fc 100644 (file)
@@ -165,8 +165,8 @@ typedef struct
 #define GinGetPosting(itup)                    ( (ItemPointer)(( ((char*)(itup)) + SHORTALIGN(GinGetOrigSizePosting(itup)) )) )
 
 #define GinMaxItemSize \
-       ((BLCKSZ - SizeOfPageHeaderData - \
-               MAXALIGN(sizeof(GinPageOpaqueData))) / 3 - sizeof(ItemIdData))
+       MAXALIGN_DOWN(((BLCKSZ - SizeOfPageHeaderData - \
+               MAXALIGN(sizeof(GinPageOpaqueData))) / 3 - sizeof(ItemIdData)))
 
 
 /*
@@ -434,8 +434,9 @@ extern void ginInsertValue(GinBtree btree, GinBtreeStack *stack);
 extern void findParents(GinBtree btree, GinBtreeStack *stack, BlockNumber rootBlkno);
 
 /* ginentrypage.c */
-extern IndexTuple GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key,
-                        ItemPointerData *ipd, uint32 nipd);
+extern IndexTuple GinFormTuple(Relation index, GinState *ginstate,
+                        OffsetNumber attnum, Datum key,
+                        ItemPointerData *ipd, uint32 nipd, bool errorTooBig);
 extern void GinShortenTuple(IndexTuple itup, uint32 nipd);
 extern void prepareEntryScan(GinBtree btree, Relation index, OffsetNumber attnum,
                                 Datum value, GinState *ginstate);