diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c new file mode 100644 index bc6911e..ee76558 *** a/src/backend/access/common/reloptions.c --- b/src/backend/access/common/reloptions.c *************** static relopt_bool boolRelOpts[] = *** 62,67 **** --- 62,75 ---- }, { { + "randomization", + "Radomize GiST insertions for bloat reducing", + RELOPT_KIND_GIST + }, + true + }, + { + { "fastupdate", "Enables \"fast update\" feature for this GIN index", RELOPT_KIND_GIN diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c new file mode 100644 index 9c6625b..3ec1e71 *** a/src/backend/access/gist/gist.c --- b/src/backend/access/gist/gist.c *************** initGISTstate(Relation index) *** 1309,1314 **** --- 1309,1315 ---- MemoryContext scanCxt; MemoryContext oldCxt; int i; + GiSTOptions *options = (GiSTOptions *) index->rd_options; /* safety check to protect fixed-size arrays in GISTSTATE */ if (index->rd_att->natts > INDEX_MAX_KEYS) *************** initGISTstate(Relation index) *** 1326,1331 **** --- 1327,1333 ---- /* Create and fill in the GISTSTATE */ giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE)); + giststate->randomization = options->randomization; giststate->scanCxt = scanCxt; giststate->tempCxt = scanCxt; /* caller must change this if needed */ giststate->tupdesc = index->rd_att; diff --git a/src/backend/access/gist/gistbuildbuffers.c b/src/backend/access/gist/gistbuildbuffers.c new file mode 100644 index fc99976..4dd93ce *** a/src/backend/access/gist/gistbuildbuffers.c --- b/src/backend/access/gist/gistbuildbuffers.c *************** gistRelocateBuildBuffersOnSplit(GISTBuil *** 547,552 **** --- 547,553 ---- IndexTuple itup; int splitPagesCount = 0, i; + int *relocationBuffersIndexes; GISTENTRY entry[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; GISTNodeBuffer oldBuf; *************** gistRelocateBuildBuffersOnSplit(GISTBuil *** 592,597 **** --- 593,600 ---- relocationBuffersInfos = (RelocationBufferInfo *) palloc(sizeof(RelocationBufferInfo) * splitPagesCount); + if (giststate->randomization) + relocationBuffersIndexes = (int *)palloc(sizeof(int) * splitPagesCount); /* * Fill relocation buffers information for node buffers of pages produced *************** gistRelocateBuildBuffersOnSplit(GISTBuil *** 654,671 **** */ best_penalty[0] = -1; /* * Loop over possible target pages, looking for one to move this tuple * to. */ for (i = 0; i < splitPagesCount; i++) { ! RelocationBufferInfo *splitPageInfo = &relocationBuffersInfos[i]; bool zero_penalty; int j; zero_penalty = true; /* Loop over index attributes. */ for (j = 0; j < r->rd_att->natts; j++) { --- 657,706 ---- */ best_penalty[0] = -1; + if (giststate->randomization) + { + /* + * If randomization id required then prepare array of buffer indexes + * in order to remember which buffers were already examined. + */ + for (i = 0; i < splitPagesCount; i++) + relocationBuffersIndexes[i] = i; + } + /* * Loop over possible target pages, looking for one to move this tuple * to. */ for (i = 0; i < splitPagesCount; i++) { ! RelocationBufferInfo *splitPageInfo; bool zero_penalty; int j; + int bufferIndex; zero_penalty = true; + if (giststate->randomization) + { + /* + * Randomization: select random buffer index from those which + * aren't previously selected. + */ + int rnd_i, tmp; + rnd_i = random() % (splitPagesCount - i) + i; + tmp = relocationBuffersIndexes[rnd_i]; + relocationBuffersIndexes[rnd_i] = relocationBuffersIndexes[i]; + relocationBuffersIndexes[i] = tmp; + bufferIndex = relocationBuffersIndexes[i]; + } + else + { + /* No randomization: select next buffer index. */ + bufferIndex = i; + } + + splitPageInfo = &relocationBuffersInfos[i]; + /* Loop over index attributes. */ for (j = 0; j < r->rd_att->natts; j++) { *************** gistRelocateBuildBuffersOnSplit(GISTBuil *** 690,696 **** * as the best for all the remaining columns during * subsequent loop iterations. */ ! which = i; best_penalty[j] = usize; if (j < r->rd_att->natts - 1) --- 725,731 ---- * as the best for all the remaining columns during * subsequent loop iterations. */ ! which = bufferIndex; best_penalty[j] = usize; if (j < r->rd_att->natts - 1) diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c new file mode 100644 index efb650e..302dc5c *** a/src/backend/access/gist/gistutil.c --- b/src/backend/access/gist/gistutil.c *************** gistchoose(Relation r, Page p, IndexTupl *** 375,380 **** --- 375,381 ---- OffsetNumber result; OffsetNumber maxoff; OffsetNumber i; + OffsetNumber offsets[MaxOffsetNumber]; float best_penalty[INDEX_MAX_KEYS]; GISTENTRY entry, identry[INDEX_MAX_KEYS]; *************** gistchoose(Relation r, Page p, IndexTupl *** 407,417 **** maxoff = PageGetMaxOffsetNumber(p); Assert(maxoff >= FirstOffsetNumber); ! for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { ! IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i)); ! bool zero_penalty; ! int j; zero_penalty = true; --- 408,450 ---- maxoff = PageGetMaxOffsetNumber(p); Assert(maxoff >= FirstOffsetNumber); ! if (giststate->randomization) { ! /* ! * If randomization id required then prepare array of offset numbers ! * in order to remember which offset numbers were already exanimed. ! */ ! for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) ! offsets[i - FirstOffsetNumber] = i; ! } ! ! for (i = 0; i < maxoff; i++) ! { ! IndexTuple itup; ! bool zero_penalty; ! int j; ! OffsetNumber offset; ! ! if (giststate->randomization) ! { ! /* ! * Randomization: select random offset number from those which ! * aren't previously selected. ! */ ! OffsetNumber rnd_i, tmp; ! rnd_i = random() % (maxoff - i) + i; ! tmp = offsets[rnd_i]; ! offsets[rnd_i] = offsets[i]; ! offsets[i] = tmp; ! offset = offsets[i]; ! } ! else ! { ! /* No randomization: select next offset number */ ! offset = i + FirstOffsetNumber; ! } ! ! itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, offset)); zero_penalty = true; *************** gistchoose(Relation r, Page p, IndexTupl *** 424,430 **** /* Compute penalty for this column. */ datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull); ! gistdentryinit(giststate, j, &entry, datum, r, p, i, FALSE, IsNull); usize = gistpenalty(giststate, j, &entry, IsNull, &identry[j], isnull[j]); --- 457,463 ---- /* Compute penalty for this column. */ datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull); ! gistdentryinit(giststate, j, &entry, datum, r, p, offset, FALSE, IsNull); usize = gistpenalty(giststate, j, &entry, IsNull, &identry[j], isnull[j]); *************** gistchoose(Relation r, Page p, IndexTupl *** 441,447 **** * adopt this tuple's penalty values as the best for all the * remaining columns during subsequent loop iterations. */ ! result = i; best_penalty[j] = usize; if (j < r->rd_att->natts - 1) --- 474,480 ---- * adopt this tuple's penalty values as the best for all the * remaining columns during subsequent loop iterations. */ ! result = offset; best_penalty[j] = usize; if (j < r->rd_att->natts - 1) *************** gistoptions(PG_FUNCTION_ARGS) *** 723,729 **** int numoptions; static const relopt_parse_elt tab[] = { {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)}, ! {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)} }; options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST, --- 756,763 ---- int numoptions; static const relopt_parse_elt tab[] = { {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)}, ! {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}, ! {"randomization", RELOPT_TYPE_BOOL, offsetof(GiSTOptions, randomization)} }; options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST, diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h new file mode 100644 index 52877ae..58dfe4c *** a/src/include/access/gist_private.h --- b/src/include/access/gist_private.h *************** typedef struct GISTSTATE *** 72,77 **** --- 72,79 ---- FmgrInfo equalFn[INDEX_MAX_KEYS]; FmgrInfo distanceFn[INDEX_MAX_KEYS]; + bool randomization; + /* Collations to pass to the support functions */ Oid supportCollation[INDEX_MAX_KEYS]; } GISTSTATE; *************** typedef struct GiSTOptions *** 408,413 **** --- 410,416 ---- int32 vl_len_; /* varlena header (do not touch directly!) */ int fillfactor; /* page fill factor in percent (0..100) */ int bufferingModeOffset; /* use buffering build? */ + bool randomization; } GiSTOptions; /* gist.c */