diff --git a/src/backend/access/gin/ginentrypage.c b/src/backend/access/gin/ginentrypage.c index c4659cde1f..87cc407233 100644 --- a/src/backend/access/gin/ginentrypage.c +++ b/src/backend/access/gin/ginentrypage.c @@ -8,20 +8,22 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gin/ginentrypage.c,v 1.21 2009/06/11 14:48:53 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/gin/ginentrypage.c,v 1.22 2009/10/02 21:14:04 tgl Exp $ *------------------------------------------------------------------------- */ #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) @@ -49,10 +51,13 @@ * 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; diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c index 9d2351af52..a5d02e74a1 100644 --- a/src/backend/access/gin/ginfast.c +++ b/src/backend/access/gin/ginfast.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gin/ginfast.c,v 1.4 2009/09/15 20:31:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/gin/ginfast.c,v 1.5 2009/10/02 21:14:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c index d175a5a99e..17f88f62d1 100644 --- a/src/backend/access/gin/gininsert.c +++ b/src/backend/access/gin/gininsert.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.23 2009/07/29 20:56:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.24 2009/10/02 21:14:04 tgl Exp $ *------------------------------------------------------------------------- */ @@ -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); diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c index 7f2ff9896f..93090efc90 100644 --- a/src/backend/access/gin/ginvacuum.c +++ b/src/backend/access/gin/ginvacuum.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.30 2009/06/11 14:48:53 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.31 2009/10/02 21:14:04 tgl Exp $ *------------------------------------------------------------------------- */ @@ -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) diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c index dd3736ad8a..a1dadfb692 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.173 2009/08/01 20:59:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.174 2009/10/02 21:14:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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."))); diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c index ff5a54ec15..c8da41076a 100644 --- a/src/backend/access/nbtree/nbtsort.c +++ b/src/backend/access/nbtree/nbtsort.c @@ -59,7 +59,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsort.c,v 1.119 2009/01/01 17:23:36 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsort.c,v 1.120 2009/10/02 21:14:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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."))); diff --git a/src/include/access/gin.h b/src/include/access/gin.h index 3f814eed73..a0d6a0d8f2 100644 --- a/src/include/access/gin.h +++ b/src/include/access/gin.h @@ -4,7 +4,7 @@ * * Copyright (c) 2006-2009, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/include/access/gin.h,v 1.34 2009/06/11 14:49:08 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/gin.h,v 1.35 2009/10/02 21:14:04 tgl Exp $ *-------------------------------------------------------------------------- */ #ifndef GIN_H @@ -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);