pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
Yet another fast GiST build
111+ messages / 19 participants
[nested] [flat]

Commits from this thread:
  2021-04-07 9f984ba6d2  Add sortsupport for gist_btree opclasses, for faster index builds.
  2021-01-17 04eb75e783  pageinspect: Fix relcache leak in gist_page_items().
  2021-01-13 5abca4b1cd  Fix test failure with wal_level=minimal.
  2021-01-13 6ecaaf810b  Fix portability issues in the new gist pageinspect test.
  2021-01-13 756ab29124  Add functions to 'pageinspect' to inspect GiST indexes.
  2020-10-30 6f0bc5e1da  Fix missing validation for the new GiST sortsupport functions.
  2020-10-01 265ea56785  Set right-links during sorted GiST index build.
  2020-09-21 c47a240fe6  Fix checksum calculation in the new sorting GiST build.
  2020-09-17 16fa9b2b30  Add support for building GiST index by sorting.
* Yet another fast GiST build
@ 2019-08-26 07:59  Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 4 replies; 111+ messages in thread

From: Andrey Borodin @ 2019-08-26 07:59 UTC (permalink / raw)
  To: Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

Hi!

In many cases GiST index can be build fast using z-order sorting.

I've looked into proof of concept by Nikita Glukhov [0] and it looks very interesting.
So, I've implemented yet another version of B-tree-like GiST build.
It's main use case and benefits can be summarized with small example:

postgres=# create table x as select point (random(),random()) from generate_series(1,3000000,1);
SELECT 3000000
Time: 5061,967 ms (00:05,062)
postgres=# create index ON x using gist (point ) with (fast_build_sort_function=gist_point_sortsupport);
CREATE INDEX
Time: 6140,227 ms (00:06,140)
postgres=# create index ON x using gist (point );
CREATE INDEX
Time: 32061,200 ms (00:32,061)

As you can see, Z-order build is on order of magnitude faster. Select performance is roughly the same. Also, index is significantly smaller.

Nikita's PoC is faster because it uses parallel build, but it intervenes into B-tree code a lot (for reuse). This patchset is GiST-isolated.
My biggest concern is that passing function to relation option seems a bit hacky. You can pass there any function matching sort support signature.
Embedding this function into opclass makes no sense: it does not affect scan anyhow.

In current version, docs and tests are not implemented. I want to discuss overall design. Do we really want yet another GiST build, if it is 3-10 times faster?

Thanks!

Best regards, Andrey Borodin.

[0] https://github.com/postgres/postgres/compare/master...glukhovn:gist_btree_build

Attachments: download all (3 files, .tar.gz)

0001-function-relopt-for-gist-build.patch (application/octet-stream, 4.9K) download From 6e1a942432f7aa43555c58e882dc404d44554e51 Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Mon, 10 Jun 2019 11:54:59 +0500 Subject: [PATCH 1/3] function relopt for gist build --- src/backend/access/common/reloptions.c | 12 +++++++++++ src/backend/access/gist/gistbuild.c | 30 ++++++++++++++++++++++++++ src/backend/access/gist/gistutil.c | 3 ++- src/include/access/gist_private.h | 2 ++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 4d0d24be0b..cd9aa97437 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -450,6 +450,18 @@ static relopt_string stringRelOpts[] = gistValidateBufferingOption, "auto" }, + { + { + "fast_build_sort_function", + "Function for presorting data instead of usual penalty\\split inserts", + RELOPT_KIND_GIST, + AccessExclusiveLock + }, + 0, + true, + gistValidateBuildFuncOption, + NULL + }, { { "check_option", diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index ecef0ff072..e7112590b8 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -22,12 +22,15 @@ #include "access/tableam.h" #include "access/xloginsert.h" #include "catalog/index.h" +#include "catalog/pg_type.h" #include "miscadmin.h" #include "optimizer/optimizer.h" +#include "parser/parse_func.h" #include "storage/bufmgr.h" #include "storage/smgr.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/regproc.h" /* Step of index tuples for check whether to switch to buffering build mode */ #define BUFFERING_MODE_SWITCH_CHECK_STEP 256 @@ -74,6 +77,7 @@ typedef struct HTAB *parentMap; GistBufferingMode bufferingMode; + FmgrInfo sortFn; } GISTBuildState; /* prototypes for private functions */ @@ -125,6 +129,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) buildstate.indexrel = index; buildstate.heaprel = heap; + buildstate.sortFn.fn_oid = InvalidOid; if (index->rd_options) { /* Get buffering mode from the options string */ @@ -138,6 +143,16 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) else buildstate.bufferingMode = GIST_BUFFERING_AUTO; + if (options->vl_len_ > offsetof(GiSTOptions, buildSortFunctionOffset) && + options->bufferingModeOffset > 0) + { + char *sortFuncName = (char *) options + options->buildSortFunctionOffset; + Oid argList[1] = {INTERNALOID}; + List *namelist = stringToQualifiedNameList(sortFuncName); + Oid sortFuncOid = LookupFuncName(namelist, 1, argList, false); + fmgr_info(sortFuncOid, &buildstate.sortFn); + } + fillfactor = options->fillfactor; } else @@ -255,6 +270,21 @@ gistValidateBufferingOption(const char *value) } } +/* + * Validator for "fast_build_sort_function" reloption on GiST indexes. Allows function name + */ +void +gistValidateBuildFuncOption(const char *value) +{ + Oid argList[1] = {INTERNALOID}; + List *namelist; + if (value == NULL) + return; + namelist = stringToQualifiedNameList(value); + /* LookupFuncName will fail if function is not existent */ + LookupFuncName(namelist, 1, argList, false); +} + /* * Attempt to switch to buffering mode. * diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 49df05653b..6e2ff95b5c 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -895,7 +895,8 @@ gistoptions(Datum reloptions, bool validate) int numoptions; static const relopt_parse_elt tab[] = { {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)}, - {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)} + {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}, + {"fast_build_sort_function", RELOPT_TYPE_STRING, offsetof(GiSTOptions, buildSortFunctionOffset)} }; options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST, diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index f80694bf9a..b4b6883f6d 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -386,6 +386,7 @@ typedef struct GiSTOptions int32 vl_len_; /* varlena header (do not touch directly!) */ int fillfactor; /* page fill factor in percent (0..100) */ int bufferingModeOffset; /* use buffering build? */ + int buildSortFunctionOffset; /* used buffering sort function */ } GiSTOptions; /* gist.c */ @@ -531,6 +532,7 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup, extern IndexBuildResult *gistbuild(Relation heap, Relation index, struct IndexInfo *indexInfo); extern void gistValidateBufferingOption(const char *value); +extern void gistValidateBuildFuncOption(const char *value); /* gistbuildbuffers.c */ extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep, -- 2.20.1 =
0003-Implement-GiST-build-using-sort-support.patch (application/octet-stream, 15.6K) download From c39f3ef547d03e02f2dfca6d011ca65aa7e6c8c3 Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Sun, 25 Aug 2019 14:18:31 +0500 Subject: [PATCH 3/3] Implement GiST build using sort support --- src/backend/access/gist/gistbuild.c | 204 +++++++++++++++++++++++++++- src/backend/access/gist/gistutil.c | 30 +++- src/backend/utils/sort/tuplesort.c | 82 +++++++++++ src/include/access/gist_private.h | 3 + src/include/utils/tuplesort.h | 6 + 5 files changed, 312 insertions(+), 13 deletions(-) diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index e7112590b8..b49875e731 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -31,6 +31,7 @@ #include "utils/memutils.h" #include "utils/rel.h" #include "utils/regproc.h" +#include "utils/tuplesort.h" /* Step of index tuples for check whether to switch to buffering build mode */ #define BUFFERING_MODE_SWITCH_CHECK_STEP 256 @@ -56,6 +57,15 @@ typedef enum GIST_BUFFERING_ACTIVE /* in buffering build mode */ } GistBufferingMode; +/* + * Status record for spooling/sorting phase. + */ +typedef struct +{ + Tuplesortstate *sortstate; /* state data for tuplesort.c */ + Relation index; +} GSpool; + /* Working state for gistbuild and its callback */ typedef struct { @@ -77,7 +87,7 @@ typedef struct HTAB *parentMap; GistBufferingMode bufferingMode; - FmgrInfo sortFn; + GSpool *spool; } GISTBuildState; /* prototypes for private functions */ @@ -111,6 +121,173 @@ static void gistMemorizeParent(GISTBuildState *buildstate, BlockNumber child, static void gistMemorizeAllDownlinks(GISTBuildState *buildstate, Buffer parent); static BlockNumber gistGetParent(GISTBuildState *buildstate, BlockNumber child); +static GSpool *gist_spoolinit(Relation heap, Relation index, SortSupport ssup); +static void gist_spool(GSpool *gspool, ItemPointer self, Datum *values, bool *isnull); +static void gist_indexsortbuild(GISTBuildState *state); +static void gist_indexsortbuild_flush(Relation rel, Page page, BlockNumber blockno, bool isNew); + +/* + * create and initialize a spool structure to sort tuples + */ +static GSpool * +gist_spoolinit(Relation heap, Relation index, SortSupport ssup) +{ + GSpool *gspool = (GSpool *) palloc0(sizeof(GSpool)); + + gspool->index = index; + + /* + * We size the sort area as maintenance_work_mem rather than work_mem to + * speed index creation. This should be OK since a single backend can't + * run multiple index creations in parallel. + */ + gspool->sortstate = tuplesort_begin_index_gist(heap, + index, + ssup, + maintenance_work_mem, + NULL, + false); + + return gspool; +} + +/* + * Flush page contents to actual page at blockno + * We have expected block number, because GiST build relies on that pages + * will be allocated in continous segments. This simplifies allocation + * logic. + */ +static void +gist_indexsortbuild_flush(Relation rel, Page page, BlockNumber blockno, bool isNew) +{ + OffsetNumber i, + maxoff; + Page newpage; + + Buffer buffer = ReadBuffer(rel, isNew ? P_NEW : blockno); + GISTInitBuffer(buffer, GistPageIsLeaf(page) ? F_LEAF : 0); + /* If the page is new - check that it was allocated correctly */ + Assert(BufferGetBlockNumber(buffer) == blockno); + + LockBuffer(buffer, GIST_EXCLUSIVE); + newpage = BufferGetPage(buffer); + + maxoff = PageGetMaxOffsetNumber(page); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + ItemId itemid = PageGetItemId(page, i); + IndexTuple tuple = (IndexTuple) PageGetItem(page, itemid); + gistfillbuffer(newpage, &tuple, + 1, InvalidOffsetNumber); + } + UnlockReleaseBuffer(buffer); +} + +/* + * given a spool loaded by successive calls to _h_spool, + * create an entire index. + */ +static void +gist_indexsortbuild(GISTBuildState *state) +{ + GSpool *gspool = state->spool; + /* + * Build constructs GiST by levels. Level is always allocated + * sequentially from level_start until level_end. + */ + BlockNumber level_start = GIST_ROOT_BLKNO + 1; + BlockNumber level_end = level_start; + BlockNumber prev_level_start; + IndexTuple itup; + /* + * We keep one page in memory for the special case + * When layer will have only on page - we will place it + * to ROOT_BLOCK_NO + */ + Page page = palloc(BLCKSZ); + gistinitpage(page, F_LEAF); + + tuplesort_performsort(gspool->sortstate); + + /* Create a first layer of leaf pages */ + while ((itup = tuplesort_getindextuple(gspool->sortstate, true)) != NULL) + { + if (PageGetFreeSpace(page) >= IndexTupleSize(itup) + sizeof(ItemIdData)) + { + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); + } + else + { + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, F_LEAF); + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); + } + } + + /* Construct internal levels */ + do + { + /* If previous level had only one page - that page is a root */ + if (level_start == level_end) + { + gist_indexsortbuild_flush(state->indexrel, page, GIST_ROOT_BLKNO, + false); + return; + } + + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, 0); + + prev_level_start = level_start; + level_start = level_end; + + for (BlockNumber i = prev_level_start; i < level_start; i++) + { + /* For each page on previous level we form one tuple */ + Buffer lower_buffer = ReadBuffer(state->indexrel, i); + Page lower_page; + IndexTuple union_tuple; + MemoryContext oldCtx = MemoryContextSwitchTo(state->giststate->tempCxt); + int vect_len; + LockBuffer(lower_buffer, GIST_SHARE); + lower_page = BufferGetPage(lower_buffer); + + IndexTuple *itvec = gistextractpage(lower_page, &vect_len); + union_tuple = gistunion(state->indexrel, itvec, vect_len, + state->giststate); + ItemPointerSetBlockNumber(&(union_tuple->t_tid), i); + + if (PageGetFreeSpace(page) >= IndexTupleSize(union_tuple) + sizeof(ItemIdData)) + { + gistfillbuffer(page, &union_tuple, 1, InvalidOffsetNumber); + } + else + { + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, 0); + gistfillbuffer(page, &union_tuple, 1, InvalidOffsetNumber); + } + + UnlockReleaseBuffer(lower_buffer); + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(state->giststate->tempCxt); + } + } while (true); +} + +/* + * spool an index entry into the sort file. + */ +static void +gist_spool(GSpool *gspool, ItemPointer self, Datum *values, bool *isnull) +{ + tuplesort_putindextuplevalues(gspool->sortstate, gspool->index, + self, values, isnull); +} + /* * Main entry point to GiST index build. Initially calls insert over and over, * but switches to more efficient buffering build algorithm after a certain @@ -129,7 +306,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) buildstate.indexrel = index; buildstate.heaprel = heap; - buildstate.sortFn.fn_oid = InvalidOid; + buildstate.spool = NULL; if (index->rd_options) { /* Get buffering mode from the options string */ @@ -144,13 +321,16 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) buildstate.bufferingMode = GIST_BUFFERING_AUTO; if (options->vl_len_ > offsetof(GiSTOptions, buildSortFunctionOffset) && - options->bufferingModeOffset > 0) + options->buildSortFunctionOffset > 0) { char *sortFuncName = (char *) options + options->buildSortFunctionOffset; Oid argList[1] = {INTERNALOID}; List *namelist = stringToQualifiedNameList(sortFuncName); Oid sortFuncOid = LookupFuncName(namelist, 1, argList, false); - fmgr_info(sortFuncOid, &buildstate.sortFn); + SortSupport sort = palloc0(sizeof(SortSupportData)); + OidFunctionCall1(sortFuncOid, PointerGetDatum(sort)); + buildstate.bufferingMode = GIST_BUFFERING_DISABLED; + buildstate.spool = gist_spoolinit(heap, index, sort); } fillfactor = options->fillfactor; @@ -223,6 +403,12 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) gistFreeBuildBuffers(buildstate.gfbb); } + if (buildstate.spool) + { + gist_indexsortbuild(&buildstate); + tuplesort_end(buildstate.spool->sortstate); + } + /* okay, all heap tuples are indexed */ MemoryContextSwitchTo(oldcxt); MemoryContextDelete(buildstate.giststate->tempCxt); @@ -498,14 +684,20 @@ gistBuildCallback(Relation index, GISTBuildState *buildstate = (GISTBuildState *) state; IndexTuple itup; MemoryContext oldCtx; + Datum compressed_values[INDEX_MAX_KEYS]; oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); /* form an index tuple and point it at the heap tuple */ - itup = gistFormTuple(buildstate->giststate, index, values, isnull, true); + itup = gistCompressValuesAndFormTuple(buildstate->giststate, index, values, isnull, true, compressed_values); itup->t_tid = htup->t_self; - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) + if (buildstate->spool) + { + if (tupleIsAlive) + gist_spool(buildstate->spool, (ItemPointer)itup, compressed_values, isnull); + } + else if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) { /* We have buffers, so use them. */ gistBufferingBuildInsert(buildstate, itup); diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 6e2ff95b5c..330ef3a2af 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -174,6 +174,7 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc, &IsNull); + if (IsNull) continue; @@ -576,6 +577,13 @@ gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf) { Datum compatt[INDEX_MAX_KEYS]; + return gistCompressValuesAndFormTuple(giststate, r, attdata, isnull, isleaf, compatt); +} + +IndexTuple +gistCompressValuesAndFormTuple(GISTSTATE *giststate, Relation r, + Datum attdata[], bool isnull[], bool isleaf, Datum compatt[]) +{ int i; IndexTuple res; @@ -746,14 +754,10 @@ gistpenalty(GISTSTATE *giststate, int attno, * Initialize a new index page */ void -GISTInitBuffer(Buffer b, uint32 f) +gistinitpage(Page page, uint32 f) { - GISTPageOpaque opaque; - Page page; - Size pageSize; - - pageSize = BufferGetPageSize(b); - page = BufferGetPage(b); + GISTPageOpaque opaque; + Size pageSize = BLCKSZ; PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); opaque = GistPageGetOpaque(page); @@ -764,6 +768,18 @@ GISTInitBuffer(Buffer b, uint32 f) opaque->gist_page_id = GIST_PAGE_ID; } +/* + * Initialize a new index buffer + */ +void +GISTInitBuffer(Buffer b, uint32 f) +{ + Page page; + + page = BufferGetPage(b); + gistinitpage(page, f); +} + /* * Verify that a freshly-read page looks sane. */ diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 7b8e67899e..3249f3eb69 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -630,6 +630,8 @@ static int comparetup_index_btree(const SortTuple *a, const SortTuple *b, Tuplesortstate *state); static int comparetup_index_hash(const SortTuple *a, const SortTuple *b, Tuplesortstate *state); +static int comparetup_index_gist(const SortTuple *a, const SortTuple *b, + Tuplesortstate *state); static void copytup_index(Tuplesortstate *state, SortTuple *stup, void *tup); static void writetup_index(Tuplesortstate *state, int tapenum, SortTuple *stup); @@ -1095,6 +1097,43 @@ tuplesort_begin_index_hash(Relation heapRel, return state; } +Tuplesortstate * +tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + SortSupport ssup, + int workMem, + SortCoordinate coordinate, + bool randomAccess) +{ + Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, + randomAccess); + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(state->sortcontext); + +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "begin index sort: workMem = %d, randomAccess = %c", + workMem, randomAccess ? 't' : 'f'); +#endif + + state->nKeys = 1; /* Only one sort column, the gist attribute */ + + state->comparetup = comparetup_index_gist; + state->copytup = copytup_index; + state->writetup = writetup_index; + state->readtup = readtup_index; + state->sortKeys = ssup; + + state->heapRel = heapRel; + state->indexRel = indexRel; + + MemoryContextSwitchTo(oldcontext); + + return state; +} + Tuplesortstate * tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, @@ -4138,6 +4177,49 @@ comparetup_index_hash(const SortTuple *a, const SortTuple *b, return 0; } +static int +comparetup_index_gist(const SortTuple *a, const SortTuple *b, + Tuplesortstate *state) +{ + SortSupport sortKey = state->sortKeys; + IndexTuple tuple1; + IndexTuple tuple2; + int32 compare; + + + /* Compare the leading sort key */ + compare = ApplySortComparator(a->datum1, a->isnull1, + b->datum1, b->isnull1, + sortKey); + if (compare != 0) + return compare; + + tuple1 = (IndexTuple) a->tuple; + tuple2 = (IndexTuple) b->tuple; + /* + * If key values are equal, we sort on ItemPointer. + */ + { + BlockNumber blk1 = ItemPointerGetBlockNumber(&tuple1->t_tid); + BlockNumber blk2 = ItemPointerGetBlockNumber(&tuple2->t_tid); + + if (blk1 != blk2) + return (blk1 < blk2) ? -1 : 1; + } + { + OffsetNumber pos1 = ItemPointerGetOffsetNumber(&tuple1->t_tid); + OffsetNumber pos2 = ItemPointerGetOffsetNumber(&tuple2->t_tid); + + if (pos1 != pos2) + return (pos1 < pos2) ? -1 : 1; + } + + /* ItemPointer values should never be equal */ + Assert(false); + + return 0; +} + static void copytup_index(Tuplesortstate *state, SortTuple *stup, void *tup) { diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index b4b6883f6d..ef9afdf1ca 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -487,12 +487,15 @@ extern IndexTuple gistgetadjusted(Relation r, GISTSTATE *giststate); extern IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf); +extern IndexTuple gistCompressValuesAndFormTuple(GISTSTATE *giststate, Relation r, + Datum attdata[], bool isnull[], bool isleaf, Datum compatt[]); extern OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate); extern void GISTInitBuffer(Buffer b, uint32 f); +extern void gistinitpage(Page page, uint32 f); extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull); diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h index 4521de18e1..3f1ceac3d2 100644 --- a/src/include/utils/tuplesort.h +++ b/src/include/utils/tuplesort.h @@ -26,6 +26,7 @@ #include "fmgr.h" #include "storage/dsm.h" #include "utils/relcache.h" +#include "utils/sortsupport.h" /* @@ -209,6 +210,11 @@ extern Tuplesortstate *tuplesort_begin_index_hash(Relation heapRel, uint32 max_buckets, int workMem, SortCoordinate coordinate, bool randomAccess); +extern Tuplesortstate *tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + SortSupport ssup, + int workMem, SortCoordinate coordinate, + bool randomAccess); extern Tuplesortstate *tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, -- 2.20.1 =
0002-Add-sort-support-for-point-gist_point_sortsupport.patch (application/octet-stream, 2.7K) download From ac8bb2980444efcfb99b9b4e22630e9becf46c4f Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Sun, 25 Aug 2019 12:42:36 +0500 Subject: [PATCH 2/3] Add sort support for point gist_point_sortsupport --- src/backend/access/gist/gistproc.c | 57 ++++++++++++++++++++++++++++++ src/include/catalog/pg_proc.dat | 3 ++ 2 files changed, 60 insertions(+) diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 1826b51bbb..e2702af280 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -24,6 +24,7 @@ #include "utils/builtins.h" #include "utils/float.h" #include "utils/geo_decls.h" +#include "utils/sortsupport.h" static bool gist_box_leaf_consistent(BOX *key, BOX *query, @@ -1530,3 +1531,59 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } + +static int64 +part_bits32_by2(uint32 x) +{ + uint64 n = x; + + n = (n | (n << 16)) & 0x0000FFFF0000FFFF; + n = (n | (n << 8)) & 0x00FF00FF00FF00FF; + n = (n | (n << 4)) & 0x0F0F0F0F0F0F0F0F; + n = (n | (n << 2)) & 0x3333333333333333; + n = (n | (n << 1)) & 0x5555555555555555; + + return n; +} + +static int64 +interleave_bits32(uint32 x, uint32 y) +{ + return part_bits32_by2(x) | (part_bits32_by2(y) << 1); +} + +static inline uint64 +point_zorder_internal(Point *p) +{ + union { + float f; + uint32 i; + } a,b; + a.f = p->x; + b.f = p->y; + return interleave_bits32(a.i, b.i); +} + +static int +gist_point_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + Point *p1 = DatumGetPointP(x); + Point *p2 = DatumGetPointP(y); + uint64 z1 = point_zorder_internal(p1); + uint64 z2 = point_zorder_internal(p2); + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +gist_point_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gist_point_fastcmp; + PG_RETURN_VOID(); +} + + +//create table x as select point (random(),random()) from generate_series(1,1000,1); +//create index ON x using gist (point ) with (fast_build_sort_function=gist_point_sortsupport); \ No newline at end of file diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 87335248a0..de3cb2f1e3 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7849,6 +7849,9 @@ proname => 'gist_poly_distance', prorettype => 'float8', proargtypes => 'internal polygon int2 oid internal', prosrc => 'gist_poly_distance' }, +{ oid => '3429', descr => 'sort support', + proname => 'gist_point_sortsupport', prorettype => 'void', + proargtypes => 'internal', prosrc => 'gist_point_sortsupport' }, # GIN array support { oid => '2743', descr => 'GIN array support', -- 2.20.1 =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-08-26 10:27  Darafei "Komяpa" Praliaskouski <me@komzpa.net>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  3 siblings, 0 replies; 111+ messages in thread

From: Darafei "Komяpa" Praliaskouski @ 2019-08-26 10:27 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: pgsql-hackers

Hello,

This is very interesting. In my pipeline currently GiST index rebuild is
the biggest time consuming step.

I believe introducing optional concept of order in the GiST opclass will be
beneficial not only for fast build, but for other tasks later:
 - CLUSTER can order the table using that notion, in parallel way.
 - btree_gist can be even closer to btree by getting the tuples sorted
inside page.
 - tree descend on insertion in future can traverse the list in more
opportunistic way, calculating penalty for siblings-by-order first.

I believe everywhere the idea of ordering is needed it's provided by giving
a btree opclass.

How about giving a link to btree opclass inside a gist opclass?


On Mon, Aug 26, 2019 at 10:59 AM Andrey Borodin <x4mmm@yandex-team.ru>
wrote:

> Hi!
>
> In many cases GiST index can be build fast using z-order sorting.
>
> I've looked into proof of concept by Nikita Glukhov [0] and it looks very
> interesting.
> So, I've implemented yet another version of B-tree-like GiST build.
> It's main use case and benefits can be summarized with small example:
>
> postgres=# create table x as select point (random(),random()) from
> generate_series(1,3000000,1);
> SELECT 3000000
> Time: 5061,967 ms (00:05,062)
> postgres=# create index ON x using gist (point ) with
> (fast_build_sort_function=gist_point_sortsupport);
> CREATE INDEX
> Time: 6140,227 ms (00:06,140)
> postgres=# create index ON x using gist (point );
> CREATE INDEX
> Time: 32061,200 ms (00:32,061)
>
> As you can see, Z-order build is on order of magnitude faster. Select
> performance is roughly the same. Also, index is significantly smaller.
>
> Nikita's PoC is faster because it uses parallel build, but it intervenes
> into B-tree code a lot (for reuse). This patchset is GiST-isolated.
> My biggest concern is that passing function to relation option seems a bit
> hacky. You can pass there any function matching sort support signature.
> Embedding this function into opclass makes no sense: it does not affect
> scan anyhow.
>
> In current version, docs and tests are not implemented. I want to discuss
> overall design. Do we really want yet another GiST build, if it is 3-10
> times faster?
>
> Thanks!
>
> Best regards, Andrey Borodin.
>
> [0]
> https://github.com/postgres/postgres/compare/master...glukhovn:gist_btree_build
>
>

-- 
Darafei Praliaskouski
Support me: http://patreon.com/komzpa

^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-08-26 10:47  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  3 siblings, 0 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2019-08-26 10:47 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On 26/08/2019 10:59, Andrey Borodin wrote:
> Hi!
> 
> In many cases GiST index can be build fast using z-order sorting.
> 
> I've looked into proof of concept by Nikita Glukhov [0] and it looks very interesting.
> So, I've implemented yet another version of B-tree-like GiST build.

Cool!

> My biggest concern is that passing function to relation option seems
> a bit hacky. You can pass there any function matching sort support
> signature. Embedding this function into opclass makes no sense: it
> does not affect scan anyhow.

I think it should be a new, optional, GiST "AM support function", in 
pg_amproc. That's how the sort support functions are defined for B-tree 
opfamilies, too.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-08-29 22:47  Alexander Korotkov <a.korotkov@postgrespro.ru>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  3 siblings, 2 replies; 111+ messages in thread

From: Alexander Korotkov @ 2019-08-29 22:47 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Mon, Aug 26, 2019 at 10:59 AM Andrey Borodin <x4mmm@yandex-team.ru> wrote:
> In many cases GiST index can be build fast using z-order sorting.
>
> I've looked into proof of concept by Nikita Glukhov [0] and it looks very interesting.
> So, I've implemented yet another version of B-tree-like GiST build.
> It's main use case and benefits can be summarized with small example:
>
> postgres=# create table x as select point (random(),random()) from generate_series(1,3000000,1);
> SELECT 3000000
> Time: 5061,967 ms (00:05,062)
> postgres=# create index ON x using gist (point ) with (fast_build_sort_function=gist_point_sortsupport);
> CREATE INDEX
> Time: 6140,227 ms (00:06,140)
> postgres=# create index ON x using gist (point );
> CREATE INDEX
> Time: 32061,200 ms (00:32,061)
>
> As you can see, Z-order build is on order of magnitude faster. Select performance is roughly the same. Also, index is significantly smaller.

Cool!  These experiments bring me to following thoughts.  Can we not
only build, but also maintain GiST indexes in B-tree-like manner?  If
we put Z-value together with MBR to the non-leaf keys, that should be
possible.  Maintaining it in B-tree-like manner would have a lot of
advantages.
1) Binary search in non-leaf pages instead of probing each key is much faster.
2) Split algorithm is also simpler and faster.
3) We can refind existing index tuple in predictable time of O(log N).
And this doesn't depend on MBR overlapping degree.  This is valuable
for future table AMs, which would have notion of deleting individual
index tuples (for instance, zheap promises to eventually support
delete-marking indexes).

Eventually, we may come to the idea of B-tree indexes with
user-defined additional keys in non-leaf tuples, which could be used
for scanning.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-08-29 23:33  Peter Geoghegan <pg@bowt.ie>
  parent: Alexander Korotkov <a.korotkov@postgrespro.ru>
  1 sibling, 1 reply; 111+ messages in thread

From: Peter Geoghegan @ 2019-08-29 23:33 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Thu, Aug 29, 2019 at 3:48 PM Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> > As you can see, Z-order build is on order of magnitude faster. Select performance is roughly the same. Also, index is significantly smaller.
>
> Cool!  These experiments bring me to following thoughts.  Can we not
> only build, but also maintain GiST indexes in B-tree-like manner?  If
> we put Z-value together with MBR to the non-leaf keys, that should be
> possible.  Maintaining it in B-tree-like manner would have a lot of
> advantages.

I'm not an expert on GiST, but that seems like it would have a lot of
advantages in the long term. It is certainly theoretically appealing.

Could this make it easier to use merge join with containment
operators? I'm thinking of things like geospatial joins, which can
generally only be performed as nested loop joins at the moment. This
is often wildly inefficient.

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-08-30 03:21  Alexander Korotkov <a.korotkov@postgrespro.ru>
  parent: Peter Geoghegan <pg@bowt.ie>
  0 siblings, 1 reply; 111+ messages in thread

From: Alexander Korotkov @ 2019-08-30 03:21 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Fri, Aug 30, 2019 at 2:34 AM Peter Geoghegan <pg@bowt.ie> wrote:
> On Thu, Aug 29, 2019 at 3:48 PM Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
> > > As you can see, Z-order build is on order of magnitude faster. Select performance is roughly the same. Also, index is significantly smaller.
> >
> > Cool!  These experiments bring me to following thoughts.  Can we not
> > only build, but also maintain GiST indexes in B-tree-like manner?  If
> > we put Z-value together with MBR to the non-leaf keys, that should be
> > possible.  Maintaining it in B-tree-like manner would have a lot of
> > advantages.
>
> I'm not an expert on GiST, but that seems like it would have a lot of
> advantages in the long term. It is certainly theoretically appealing.
>
> Could this make it easier to use merge join with containment
> operators? I'm thinking of things like geospatial joins, which can
> generally only be performed as nested loop joins at the moment. This
> is often wildly inefficient.

AFAICS, spatial joins aren't going to be as easy as just merge joins
on Z-value.  When searching for close points in two datasets (closer
than given threshold) we can scan some ranges of Z-value in one
dataset while iterating on another.  But dealing with prolonged
spatial objects in not that easy.  In order to determine if there are
matching values in given Z-range you also need to be aware on size of
objects inside that Z-range.  So, before merge-like join you need to
not only sort, but build some index-like structure.

Alternatively you can encode size in Z-value.  But this increases
dimensionality of space and decreases efficiency of join.  Also,
spatial join can be made using two indexes, even just current GiST
without Z-values.  We've prototyped that, see [1].

Links
1. https://github.com/pgsphere/pgsphere/blob/crossmatch_cnode/crossmatch.c

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-08-30 03:28  Peter Geoghegan <pg@bowt.ie>
  parent: Alexander Korotkov <a.korotkov@postgrespro.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Peter Geoghegan @ 2019-08-30 03:28 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Thu, Aug 29, 2019 at 8:22 PM Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> Alternatively you can encode size in Z-value.  But this increases
> dimensionality of space and decreases efficiency of join.  Also,
> spatial join can be made using two indexes, even just current GiST
> without Z-values.  We've prototyped that, see [1].

I'm pretty sure that spatial joins generally need two spatial indexes
(usually R-Trees). There seems to have been quite a lot of research in
it in the 1990s.

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-08-30 11:44  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Alexander Korotkov <a.korotkov@postgrespro.ru>
  1 sibling, 2 replies; 111+ messages in thread

From: Andrey Borodin @ 2019-08-30 11:44 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers



> 30 авг. 2019 г., в 3:47, Alexander Korotkov <a.korotkov@postgrespro.ru> написал(а):
> 
> 1) Binary search in non-leaf pages instead of probing each key is much faster.

That's a neat idea, but key union breaks ordering, even for z-order.
for two sets of tuples X and Y
if for any i,o from N, Xi < Yo
does not guaranty union(X) < union (Y) 

For example consider this z-ordered keyspace (picture attached)


union(5, 9) is z-order-smaller than union(4,4)

I'm not even sure we can use sorted search for choosing subtree for insertion.

How do you think, should I supply GiST-build patch with docs and tests and add it to CF? Or do we need more design discussion before?


Best regards, Andrey Borodin.=

Attachments:

PastedGraphic-2.png (image/png, 86.8K) download view image
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-09-01 10:53  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  1 sibling, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2019-09-01 10:53 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers



> 30 авг. 2019 г., в 16:44, Andrey Borodin <x4mmm@yandex-team.ru> написал(а):
> 
> 
> How do you think, should I supply GiST-build patch with docs and tests and add it to CF? Or do we need more design discussion before?

PFA v2: now sort support is part of opclass.
There's a problem with Z-ordering NaN which causes regression tests to fail. So I decided not to add patch to CF (despite having few minutes to do so).
How to correctly Z-order NaN? So that it would be consistent with semantics of union() and consistent() functions. If one of values is NaN, then we consider all it's bits to be 1?

BTW patch uses 
union {
        float f;
        uint32 i;
    }
I hope it's OK, because AFAIK we do not have non-IEEE-754 platforms now.

Thanks!

Best regards, Andrey Borodin.

Attachments: download all (2 files, .tar.gz)

v2-0001-Add-sort-support-for-point-gist_point_sortsupport.patch (application/octet-stream, 2.5K) download From 3f9d4f0b407339f9e40fc4ccc39d594f95ac84ca Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Sun, 25 Aug 2019 12:42:36 +0500 Subject: [PATCH 1/2] Add sort support for point gist_point_sortsupport --- src/backend/access/gist/gistproc.c | 53 ++++++++++++++++++++++++++++++ src/include/catalog/pg_proc.dat | 3 ++ 2 files changed, 56 insertions(+) diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 1826b51bbb..e53c63dc2b 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -24,6 +24,7 @@ #include "utils/builtins.h" #include "utils/float.h" #include "utils/geo_decls.h" +#include "utils/sortsupport.h" static bool gist_box_leaf_consistent(BOX *key, BOX *query, @@ -1530,3 +1531,55 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } + +static int64 +part_bits32_by2(uint32 x) +{ + uint64 n = x; + + n = (n | (n << 16)) & 0x0000FFFF0000FFFF; + n = (n | (n << 8)) & 0x00FF00FF00FF00FF; + n = (n | (n << 4)) & 0x0F0F0F0F0F0F0F0F; + n = (n | (n << 2)) & 0x3333333333333333; + n = (n | (n << 1)) & 0x5555555555555555; + + return n; +} + +static int64 +interleave_bits32(uint32 x, uint32 y) +{ + return part_bits32_by2(x) | (part_bits32_by2(y) << 1); +} + +static inline uint64 +point_zorder_internal(Point *p) +{ + union { + float f; + uint32 i; + } a,b; + a.f = p->x; + b.f = p->y; + return interleave_bits32(a.i, b.i); +} + +static int +gist_point_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + Point *p1 = DatumGetPointP(x); + Point *p2 = DatumGetPointP(y); + uint64 z1 = point_zorder_internal(p1); + uint64 z2 = point_zorder_internal(p2); + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +gist_point_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gist_point_fastcmp; + PG_RETURN_VOID(); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 87335248a0..de3cb2f1e3 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7849,6 +7849,9 @@ proname => 'gist_poly_distance', prorettype => 'float8', proargtypes => 'internal polygon int2 oid internal', prosrc => 'gist_poly_distance' }, +{ oid => '3429', descr => 'sort support', + proname => 'gist_point_sortsupport', prorettype => 'void', + proargtypes => 'internal', prosrc => 'gist_point_sortsupport' }, # GIN array support { oid => '2743', descr => 'GIN array support', -- 2.20.1 =
v2-0002-Implement-GiST-build-using-sort-support.patch (application/octet-stream, 16.5K) download From ca02781294be9b943ab1492fc2cf4b8b5b852244 Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Sun, 25 Aug 2019 14:18:31 +0500 Subject: [PATCH 2/2] Implement GiST build using sort support --- src/backend/access/gist/gistbuild.c | 219 ++++++++++++++++++++++++- src/backend/access/gist/gistutil.c | 30 +++- src/backend/access/gist/gistvalidate.c | 7 +- src/backend/utils/sort/tuplesort.c | 37 +++++ src/include/access/gist.h | 3 +- src/include/access/gist_private.h | 3 + src/include/catalog/pg_amproc.dat | 2 + src/include/utils/tuplesort.h | 6 + 8 files changed, 295 insertions(+), 12 deletions(-) diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index ecef0ff072..8a9af71816 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -28,6 +28,8 @@ #include "storage/smgr.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/regproc.h" +#include "utils/tuplesort.h" /* Step of index tuples for check whether to switch to buffering build mode */ #define BUFFERING_MODE_SWITCH_CHECK_STEP 256 @@ -53,6 +55,15 @@ typedef enum GIST_BUFFERING_ACTIVE /* in buffering build mode */ } GistBufferingMode; +/* + * Status record for spooling/sorting phase. + */ +typedef struct +{ + Tuplesortstate *sortstate; /* state data for tuplesort.c */ + Relation index; +} GSpool; + /* Working state for gistbuild and its callback */ typedef struct { @@ -74,6 +85,7 @@ typedef struct HTAB *parentMap; GistBufferingMode bufferingMode; + GSpool *spool; } GISTBuildState; /* prototypes for private functions */ @@ -107,6 +119,173 @@ static void gistMemorizeParent(GISTBuildState *buildstate, BlockNumber child, static void gistMemorizeAllDownlinks(GISTBuildState *buildstate, Buffer parent); static BlockNumber gistGetParent(GISTBuildState *buildstate, BlockNumber child); +static GSpool *gist_spoolinit(Relation heap, Relation index, SortSupport ssup); +static void gist_spool(GSpool *gspool, ItemPointer self, Datum *values, bool *isnull); +static void gist_indexsortbuild(GISTBuildState *state); +static void gist_indexsortbuild_flush(Relation rel, Page page, BlockNumber blockno, bool isNew); + +/* + * create and initialize a spool structure to sort tuples + */ +static GSpool * +gist_spoolinit(Relation heap, Relation index, SortSupport ssup) +{ + GSpool *gspool = (GSpool *) palloc0(sizeof(GSpool)); + + gspool->index = index; + + /* + * We size the sort area as maintenance_work_mem rather than work_mem to + * speed index creation. This should be OK since a single backend can't + * run multiple index creations in parallel. + */ + gspool->sortstate = tuplesort_begin_index_gist(heap, + index, + ssup, + maintenance_work_mem, + NULL, + false); + + return gspool; +} + +/* + * Flush page contents to actual page at blockno + * We have expected block number, because GiST build relies on that pages + * will be allocated in continous segments. This simplifies allocation + * logic. + */ +static void +gist_indexsortbuild_flush(Relation rel, Page page, BlockNumber blockno, bool isNew) +{ + OffsetNumber i, + maxoff; + Page newpage; + + Buffer buffer = ReadBuffer(rel, isNew ? P_NEW : blockno); + GISTInitBuffer(buffer, GistPageIsLeaf(page) ? F_LEAF : 0); + /* If the page is new - check that it was allocated correctly */ + Assert(BufferGetBlockNumber(buffer) == blockno); + + LockBuffer(buffer, GIST_EXCLUSIVE); + newpage = BufferGetPage(buffer); + + maxoff = PageGetMaxOffsetNumber(page); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + ItemId itemid = PageGetItemId(page, i); + IndexTuple tuple = (IndexTuple) PageGetItem(page, itemid); + gistfillbuffer(newpage, &tuple, + 1, InvalidOffsetNumber); + } + UnlockReleaseBuffer(buffer); +} + +/* + * given a spool loaded by successive calls to _h_spool, + * create an entire index. + */ +static void +gist_indexsortbuild(GISTBuildState *state) +{ + GSpool *gspool = state->spool; + /* + * Build constructs GiST by levels. Level is always allocated + * sequentially from level_start until level_end. + */ + BlockNumber level_start = GIST_ROOT_BLKNO + 1; + BlockNumber level_end = level_start; + BlockNumber prev_level_start; + IndexTuple itup; + /* + * We keep one page in memory for the special case + * When layer will have only on page - we will place it + * to ROOT_BLOCK_NO + */ + Page page = palloc(BLCKSZ); + gistinitpage(page, F_LEAF); + + tuplesort_performsort(gspool->sortstate); + + /* Create a first layer of leaf pages */ + while ((itup = tuplesort_getindextuple(gspool->sortstate, true)) != NULL) + { + if (PageGetFreeSpace(page) >= IndexTupleSize(itup) + sizeof(ItemIdData) + state->freespace) + { + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); + } + else + { + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, F_LEAF); + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); + } + } + + /* Construct internal levels */ + do + { + /* If previous level had only one page - that page is a root */ + if (level_start == level_end) + { + gist_indexsortbuild_flush(state->indexrel, page, GIST_ROOT_BLKNO, + false); + return; + } + + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, 0); + + prev_level_start = level_start; + level_start = level_end; + + for (BlockNumber i = prev_level_start; i < level_start; i++) + { + /* For each page on previous level we form one tuple */ + Buffer lower_buffer = ReadBuffer(state->indexrel, i); + Page lower_page; + IndexTuple union_tuple; + MemoryContext oldCtx = MemoryContextSwitchTo(state->giststate->tempCxt); + int vect_len; + LockBuffer(lower_buffer, GIST_SHARE); + lower_page = BufferGetPage(lower_buffer); + + IndexTuple *itvec = gistextractpage(lower_page, &vect_len); + union_tuple = gistunion(state->indexrel, itvec, vect_len, + state->giststate); + ItemPointerSetBlockNumber(&(union_tuple->t_tid), i); + + if (PageGetFreeSpace(page) >= IndexTupleSize(union_tuple) + sizeof(ItemIdData) + state->freespace) + { + gistfillbuffer(page, &union_tuple, 1, InvalidOffsetNumber); + } + else + { + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, 0); + gistfillbuffer(page, &union_tuple, 1, InvalidOffsetNumber); + } + + UnlockReleaseBuffer(lower_buffer); + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(state->giststate->tempCxt); + } + } while (true); +} + +/* + * spool an index entry into the sort file. + */ +static void +gist_spool(GSpool *gspool, ItemPointer self, Datum *values, bool *isnull) +{ + tuplesort_putindextuplevalues(gspool->sortstate, gspool->index, + self, values, isnull); +} + /* * Main entry point to GiST index build. Initially calls insert over and over, * but switches to more efficient buffering build algorithm after a certain @@ -121,10 +300,14 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) Buffer buffer; Page page; MemoryContext oldcxt = CurrentMemoryContext; - int fillfactor; + int fillfactor, i; + Oid SortSupportFnOids[INDEX_MAX_KEYS]; + bool hasallsortsupports = true; + int keyscount = IndexRelationGetNumberOfKeyAttributes(index); buildstate.indexrel = index; buildstate.heaprel = heap; + buildstate.spool = NULL; if (index->rd_options) { /* Get buffering mode from the options string */ @@ -152,6 +335,24 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) /* Calculate target amount of free space to leave on pages */ buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100; + for (i = 0; i < keyscount; i++) + { + SortSupportFnOids[i] = index_getprocid(index, i + 1, GIST_SORTSUPPORT_PROC); + if (!OidIsValid(SortSupportFnOids[i])) + { + hasallsortsupports = false; + break; + } + } + if (hasallsortsupports) + { + SortSupport sort = palloc0(sizeof(SortSupportData) * keyscount); + for (i = 0; i < keyscount; i++) + OidFunctionCall1(SortSupportFnOids[i], PointerGetDatum(sort + i)); + buildstate.bufferingMode = GIST_BUFFERING_DISABLED; + buildstate.spool = gist_spoolinit(heap, index, sort); + } + /* * We expect to be called exactly once for any index relation. If that's * not the case, big trouble's what we have. @@ -208,6 +409,12 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) gistFreeBuildBuffers(buildstate.gfbb); } + if (buildstate.spool) + { + gist_indexsortbuild(&buildstate); + tuplesort_end(buildstate.spool->sortstate); + } + /* okay, all heap tuples are indexed */ MemoryContextSwitchTo(oldcxt); MemoryContextDelete(buildstate.giststate->tempCxt); @@ -468,14 +675,20 @@ gistBuildCallback(Relation index, GISTBuildState *buildstate = (GISTBuildState *) state; IndexTuple itup; MemoryContext oldCtx; + Datum compressed_values[INDEX_MAX_KEYS]; oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); /* form an index tuple and point it at the heap tuple */ - itup = gistFormTuple(buildstate->giststate, index, values, isnull, true); + itup = gistCompressValuesAndFormTuple(buildstate->giststate, index, values, isnull, true, compressed_values); itup->t_tid = htup->t_self; - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) + if (buildstate->spool) + { + if (tupleIsAlive) + gist_spool(buildstate->spool, (ItemPointer)itup, compressed_values, isnull); + } + else if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) { /* We have buffers, so use them. */ gistBufferingBuildInsert(buildstate, itup); diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 49df05653b..57fc479086 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -174,6 +174,7 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc, &IsNull); + if (IsNull) continue; @@ -576,6 +577,13 @@ gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf) { Datum compatt[INDEX_MAX_KEYS]; + return gistCompressValuesAndFormTuple(giststate, r, attdata, isnull, isleaf, compatt); +} + +IndexTuple +gistCompressValuesAndFormTuple(GISTSTATE *giststate, Relation r, + Datum attdata[], bool isnull[], bool isleaf, Datum compatt[]) +{ int i; IndexTuple res; @@ -746,14 +754,10 @@ gistpenalty(GISTSTATE *giststate, int attno, * Initialize a new index page */ void -GISTInitBuffer(Buffer b, uint32 f) +gistinitpage(Page page, uint32 f) { - GISTPageOpaque opaque; - Page page; - Size pageSize; - - pageSize = BufferGetPageSize(b); - page = BufferGetPage(b); + GISTPageOpaque opaque; + Size pageSize = BLCKSZ; PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); opaque = GistPageGetOpaque(page); @@ -764,6 +768,18 @@ GISTInitBuffer(Buffer b, uint32 f) opaque->gist_page_id = GIST_PAGE_ID; } +/* + * Initialize a new index buffer + */ +void +GISTInitBuffer(Buffer b, uint32 f) +{ + Page page; + + page = BufferGetPage(b); + gistinitpage(page, f); +} + /* * Verify that a freshly-read page looks sane. */ diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index dfc1a87a75..bc65b9c6bf 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -140,6 +140,10 @@ gistvalidate(Oid opclassoid) 5, 5, INTERNALOID, opcintype, INT2OID, OIDOID, INTERNALOID); break; + case GIST_SORTSUPPORT_PROC: + ok = check_amproc_signature(procform->amproc, VOIDOID, true, + 1, 1, INTERNALOID); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -259,7 +263,8 @@ gistvalidate(Oid opclassoid) (opclassgroup->functionset & (((uint64) 1) << i)) != 0) continue; /* got it */ if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC || - i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC) + i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC || + i == GIST_SORTSUPPORT_PROC) continue; /* optional methods */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 7b8e67899e..018901b654 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -1095,6 +1095,43 @@ tuplesort_begin_index_hash(Relation heapRel, return state; } +Tuplesortstate * +tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + SortSupport ssup, + int workMem, + SortCoordinate coordinate, + bool randomAccess) +{ + Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, + randomAccess); + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(state->sortcontext); + +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "begin index sort: workMem = %d, randomAccess = %c", + workMem, randomAccess ? 't' : 'f'); +#endif + + state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); + + state->comparetup = comparetup_index_btree; + state->copytup = copytup_index; + state->writetup = writetup_index; + state->readtup = readtup_index; + state->sortKeys = ssup; + + state->heapRel = heapRel; + state->indexRel = indexRel; + + MemoryContextSwitchTo(oldcontext); + + return state; +} + Tuplesortstate * tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 6902f4115b..eeea53a8f1 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -34,7 +34,8 @@ #define GIST_EQUAL_PROC 7 #define GIST_DISTANCE_PROC 8 #define GIST_FETCH_PROC 9 -#define GISTNProcs 9 +#define GIST_SORTSUPPORT_PROC 10 +#define GISTNProcs 10 /* * Page opaque data in a GiST index page. diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index f80694bf9a..b7b6ca13ac 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -486,12 +486,15 @@ extern IndexTuple gistgetadjusted(Relation r, GISTSTATE *giststate); extern IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf); +extern IndexTuple gistCompressValuesAndFormTuple(GISTSTATE *giststate, Relation r, + Datum attdata[], bool isnull[], bool isleaf, Datum compatt[]); extern OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate); extern void GISTInitBuffer(Buffer b, uint32 f); +extern void gistinitpage(Page page, uint32 f); extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull); diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 020b7413cc..1a52302f7e 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -409,6 +409,8 @@ amproc => 'gist_point_distance' }, { amprocfamily => 'gist/point_ops', amproclefttype => 'point', amprocrighttype => 'point', amprocnum => '9', amproc => 'gist_point_fetch' }, +{ amprocfamily => 'gist/point_ops', amproclefttype => 'point', + amprocrighttype => 'point', amprocnum => '10', amproc => 'gist_point_sortsupport' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', amproc => 'gist_box_consistent' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h index 4521de18e1..3f1ceac3d2 100644 --- a/src/include/utils/tuplesort.h +++ b/src/include/utils/tuplesort.h @@ -26,6 +26,7 @@ #include "fmgr.h" #include "storage/dsm.h" #include "utils/relcache.h" +#include "utils/sortsupport.h" /* @@ -209,6 +210,11 @@ extern Tuplesortstate *tuplesort_begin_index_hash(Relation heapRel, uint32 max_buckets, int workMem, SortCoordinate coordinate, bool randomAccess); +extern Tuplesortstate *tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + SortSupport ssup, + int workMem, SortCoordinate coordinate, + bool randomAccess); extern Tuplesortstate *tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, -- 2.20.1 =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-09-01 17:17  Alexander Korotkov <a.korotkov@postgrespro.ru>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  1 sibling, 0 replies; 111+ messages in thread

From: Alexander Korotkov @ 2019-09-01 17:17 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Fri, Aug 30, 2019 at 2:44 PM Andrey Borodin <x4mmm@yandex-team.ru> wrote:
>
> 30 авг. 2019 г., в 3:47, Alexander Korotkov <a.korotkov@postgrespro.ru> написал(а):
>
> 1) Binary search in non-leaf pages instead of probing each key is much faster.
>
>
> That's a neat idea, but key union breaks ordering, even for z-order.
> for two sets of tuples X and Y
> if for any i,o from N, Xi < Yo
> does not guaranty union(X) < union (Y)
>
> For example consider this z-ordered keyspace (picture attached)
>
> union(5, 9) is z-order-smaller than union(4,4)
>
> I'm not even sure we can use sorted search for choosing subtree for insertion.


Sorry, I didn't explain my proposal in enough details.  I didn't mean
B-tree separator keys would be the same as union key (MBR).  I mean
B-tree on Z-values, which maintains union key in addition to separator
keys.  So, you select downlink to insert using separator Z-values and
then also extend union key (if needed).  It's probably still not
enough detail yet.  I'll try to spend more time for more detailed
description later.

>
> How do you think, should I supply GiST-build patch with docs and tests and add it to CF? Or do we need more design discussion before?


+1 for adding to CF.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-09-01 17:20  Alexander Korotkov <a.korotkov@postgrespro.ru>
  parent: Peter Geoghegan <pg@bowt.ie>
  0 siblings, 0 replies; 111+ messages in thread

From: Alexander Korotkov @ 2019-09-01 17:20 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Fri, Aug 30, 2019 at 6:28 AM Peter Geoghegan <pg@bowt.ie> wrote:
> On Thu, Aug 29, 2019 at 8:22 PM Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
> > Alternatively you can encode size in Z-value.  But this increases
> > dimensionality of space and decreases efficiency of join.  Also,
> > spatial join can be made using two indexes, even just current GiST
> > without Z-values.  We've prototyped that, see [1].
>
> I'm pretty sure that spatial joins generally need two spatial indexes
> (usually R-Trees). There seems to have been quite a lot of research in
> it in the 1990s.

Sure, our prototype was an implementation of one of such papers.  My
point is that advantages of Z-value ordered GiST for spatial joins are
not yet clear for me.  Except faster build and smaller index, which
are general advantages.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-09-08 08:54  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2019-09-08 08:54 UTC (permalink / raw)
  To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers



> 1 сент. 2019 г., в 15:53, Andrey Borodin <x4mmm@yandex-team.ru> написал(а):
> 
> 
> <v2-0001-Add-sort-support-for-point-gist_point_sortsupport.patch><v2-0002-Implement-GiST-build-using-sort-support.patch>

Here's V3 of the patch set.
Changes:
1. Added some documentation of new sort support routines
2. Fixed bug with dirty pages

I did not add sort support procs to built-in boxes, circles and polys, since it may be not optimal index for them. However, for points Z-order is quite good as a default.

Tests only pass with fixes for GiST KNN from Alexander in other thread.

Thanks!

Best regards, Andrey Borodin.

Attachments: download all (2 files, .tar.gz)

v3-0002-Implement-GiST-build-using-sort-support.patch (application/octet-stream, 21.0K) download From 88a076a019a28b74b14302b50b8485cb85a1c8c4 Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Sun, 25 Aug 2019 14:18:31 +0500 Subject: [PATCH 2/2] Implement GiST build using sort support --- doc/src/sgml/gist.sgml | 51 +++++- src/backend/access/gist/gistbuild.c | 220 ++++++++++++++++++++++++- src/backend/access/gist/gistproc.c | 21 ++- src/backend/access/gist/gistutil.c | 30 +++- src/backend/access/gist/gistvalidate.c | 7 +- src/backend/utils/sort/tuplesort.c | 37 +++++ src/include/access/gist.h | 3 +- src/include/access/gist_private.h | 3 + src/include/catalog/pg_amproc.dat | 2 + src/include/utils/tuplesort.h | 6 + 10 files changed, 362 insertions(+), 18 deletions(-) diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml index 44a3b2c03c..bd799d72cf 100644 --- a/doc/src/sgml/gist.sgml +++ b/doc/src/sgml/gist.sgml @@ -268,7 +268,7 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); <para> There are five methods that an index operator class for - <acronym>GiST</acronym> must provide, and four that are optional. + <acronym>GiST</acronym> must provide, and five that are optional. Correctness of the index is ensured by proper implementation of the <function>same</function>, <function>consistent</function> and <function>union</function> methods, while efficiency (size and speed) of the @@ -286,7 +286,8 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); if the operator class wishes to support ordered scans (nearest-neighbor searches). The optional ninth method <function>fetch</function> is needed if the operator class wishes to support index-only scans, except when the - <function>compress</function> method is omitted. + <function>compress</function> method is omitted. The <function>sortsupport</function> + method is also optional and is used diring fast GiST build. </para> <variablelist> @@ -938,6 +939,52 @@ my_fetch(PG_FUNCTION_ARGS) </listitem> </varlistentry> + <varlistentry> + <term><function>sortsupport</function></term> + <listitem> + <para> + Returns a comparator, suitable for fast GiST build. + </para> + + <para> + The <acronym>SQL</acronym> declaration of the function must look like this: + +<programlisting> +CREATE OR REPLACE FUNCTION my_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; +</programlisting> + + The argument is a pointer to a <structname>SortSupport</structname> struct. + </para> + + <para> + The matching code in the C module could then follow this skeleton: + +<programlisting> +PG_FUNCTION_INFO_V1(my_sortsupport); + +static int +my_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + /* esteblish order between x and y */ + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +my_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = my_fastcmp; + PG_RETURN_VOID(); +} +</programlisting> + </para> + </listitem> + </varlistentry> </variablelist> <para> diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index ecef0ff072..bff44667dd 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -28,6 +28,8 @@ #include "storage/smgr.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/regproc.h" +#include "utils/tuplesort.h" /* Step of index tuples for check whether to switch to buffering build mode */ #define BUFFERING_MODE_SWITCH_CHECK_STEP 256 @@ -53,6 +55,15 @@ typedef enum GIST_BUFFERING_ACTIVE /* in buffering build mode */ } GistBufferingMode; +/* + * Status record for spooling/sorting phase. + */ +typedef struct +{ + Tuplesortstate *sortstate; /* state data for tuplesort.c */ + Relation index; +} GSpool; + /* Working state for gistbuild and its callback */ typedef struct { @@ -74,6 +85,7 @@ typedef struct HTAB *parentMap; GistBufferingMode bufferingMode; + GSpool *spool; } GISTBuildState; /* prototypes for private functions */ @@ -107,6 +119,174 @@ static void gistMemorizeParent(GISTBuildState *buildstate, BlockNumber child, static void gistMemorizeAllDownlinks(GISTBuildState *buildstate, Buffer parent); static BlockNumber gistGetParent(GISTBuildState *buildstate, BlockNumber child); +static GSpool *gist_spoolinit(Relation heap, Relation index, SortSupport ssup); +static void gist_spool(GSpool *gspool, ItemPointer self, Datum *values, bool *isnull); +static void gist_indexsortbuild(GISTBuildState *state); +static void gist_indexsortbuild_flush(Relation rel, Page page, BlockNumber blockno, bool isNew); + +/* + * create and initialize a spool structure to sort tuples + */ +static GSpool * +gist_spoolinit(Relation heap, Relation index, SortSupport ssup) +{ + GSpool *gspool = (GSpool *) palloc0(sizeof(GSpool)); + + gspool->index = index; + + /* + * We size the sort area as maintenance_work_mem rather than work_mem to + * speed index creation. This should be OK since a single backend can't + * run multiple index creations in parallel. + */ + gspool->sortstate = tuplesort_begin_index_gist(heap, + index, + ssup, + maintenance_work_mem, + NULL, + false); + + return gspool; +} + +/* + * Flush page contents to actual page at blockno + * We have expected block number, because GiST build relies on that pages + * will be allocated in continous segments. This simplifies allocation + * logic. + */ +static void +gist_indexsortbuild_flush(Relation rel, Page page, BlockNumber blockno, bool isNew) +{ + OffsetNumber i, + maxoff; + Page newpage; + + Buffer buffer = ReadBuffer(rel, isNew ? P_NEW : blockno); + GISTInitBuffer(buffer, GistPageIsLeaf(page) ? F_LEAF : 0); + /* If the page is new - check that it was allocated correctly */ + Assert(BufferGetBlockNumber(buffer) == blockno); + + LockBuffer(buffer, GIST_EXCLUSIVE); + newpage = BufferGetPage(buffer); + + maxoff = PageGetMaxOffsetNumber(page); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + ItemId itemid = PageGetItemId(page, i); + IndexTuple tuple = (IndexTuple) PageGetItem(page, itemid); + gistfillbuffer(newpage, &tuple, + 1, InvalidOffsetNumber); + } + MarkBufferDirty(buffer); + UnlockReleaseBuffer(buffer); +} + +/* + * given a spool loaded by successive calls to _h_spool, + * create an entire index. + */ +static void +gist_indexsortbuild(GISTBuildState *state) +{ + GSpool *gspool = state->spool; + /* + * Build constructs GiST by levels. Level is always allocated + * sequentially from level_start until level_end. + */ + BlockNumber level_start = GIST_ROOT_BLKNO + 1; + BlockNumber level_end = level_start; + BlockNumber prev_level_start; + IndexTuple itup; + /* + * We keep one page in memory for the special case + * When layer will have only on page - we will place it + * to ROOT_BLOCK_NO + */ + Page page = palloc(BLCKSZ); + gistinitpage(page, F_LEAF); + + tuplesort_performsort(gspool->sortstate); + + /* Create a first layer of leaf pages */ + while ((itup = tuplesort_getindextuple(gspool->sortstate, true)) != NULL) + { + if (PageGetFreeSpace(page) >= IndexTupleSize(itup) + sizeof(ItemIdData) + state->freespace) + { + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); + } + else + { + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, F_LEAF); + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); + } + } + + /* Construct internal levels */ + do + { + /* If previous level had only one page - that page is a root */ + if (level_start == level_end) + { + gist_indexsortbuild_flush(state->indexrel, page, GIST_ROOT_BLKNO, + false); + return; + } + + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, 0); + + prev_level_start = level_start; + level_start = level_end; + + for (BlockNumber i = prev_level_start; i < level_start; i++) + { + /* For each page on previous level we form one tuple */ + Buffer lower_buffer = ReadBuffer(state->indexrel, i); + Page lower_page; + IndexTuple union_tuple; + MemoryContext oldCtx = MemoryContextSwitchTo(state->giststate->tempCxt); + int vect_len; + LockBuffer(lower_buffer, GIST_SHARE); + lower_page = BufferGetPage(lower_buffer); + + IndexTuple *itvec = gistextractpage(lower_page, &vect_len); + union_tuple = gistunion(state->indexrel, itvec, vect_len, + state->giststate); + ItemPointerSetBlockNumber(&(union_tuple->t_tid), i); + + if (PageGetFreeSpace(page) >= IndexTupleSize(union_tuple) + sizeof(ItemIdData) + state->freespace) + { + gistfillbuffer(page, &union_tuple, 1, InvalidOffsetNumber); + } + else + { + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, 0); + gistfillbuffer(page, &union_tuple, 1, InvalidOffsetNumber); + } + + UnlockReleaseBuffer(lower_buffer); + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(state->giststate->tempCxt); + } + } while (true); +} + +/* + * spool an index entry into the sort file. + */ +static void +gist_spool(GSpool *gspool, ItemPointer self, Datum *values, bool *isnull) +{ + tuplesort_putindextuplevalues(gspool->sortstate, gspool->index, + self, values, isnull); +} + /* * Main entry point to GiST index build. Initially calls insert over and over, * but switches to more efficient buffering build algorithm after a certain @@ -121,10 +301,14 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) Buffer buffer; Page page; MemoryContext oldcxt = CurrentMemoryContext; - int fillfactor; + int fillfactor, i; + Oid SortSupportFnOids[INDEX_MAX_KEYS]; + bool hasallsortsupports = true; + int keyscount = IndexRelationGetNumberOfKeyAttributes(index); buildstate.indexrel = index; buildstate.heaprel = heap; + buildstate.spool = NULL; if (index->rd_options) { /* Get buffering mode from the options string */ @@ -152,6 +336,24 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) /* Calculate target amount of free space to leave on pages */ buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100; + for (i = 0; i < keyscount; i++) + { + SortSupportFnOids[i] = index_getprocid(index, i + 1, GIST_SORTSUPPORT_PROC); + if (!OidIsValid(SortSupportFnOids[i])) + { + hasallsortsupports = false; + break; + } + } + if (hasallsortsupports) + { + SortSupport sort = palloc0(sizeof(SortSupportData) * keyscount); + for (i = 0; i < keyscount; i++) + OidFunctionCall1(SortSupportFnOids[i], PointerGetDatum(sort + i)); + buildstate.bufferingMode = GIST_BUFFERING_DISABLED; + buildstate.spool = gist_spoolinit(heap, index, sort); + } + /* * We expect to be called exactly once for any index relation. If that's * not the case, big trouble's what we have. @@ -208,6 +410,12 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) gistFreeBuildBuffers(buildstate.gfbb); } + if (buildstate.spool) + { + gist_indexsortbuild(&buildstate); + tuplesort_end(buildstate.spool->sortstate); + } + /* okay, all heap tuples are indexed */ MemoryContextSwitchTo(oldcxt); MemoryContextDelete(buildstate.giststate->tempCxt); @@ -468,14 +676,20 @@ gistBuildCallback(Relation index, GISTBuildState *buildstate = (GISTBuildState *) state; IndexTuple itup; MemoryContext oldCtx; + Datum compressed_values[INDEX_MAX_KEYS]; oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); /* form an index tuple and point it at the heap tuple */ - itup = gistFormTuple(buildstate->giststate, index, values, isnull, true); + itup = gistCompressValuesAndFormTuple(buildstate->giststate, index, values, isnull, true, compressed_values); itup->t_tid = htup->t_self; - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) + if (buildstate->spool) + { + if (tupleIsAlive) + gist_spool(buildstate->spool, (ItemPointer)itup, compressed_values, isnull); + } + else if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) { /* We have buffers, so use them. */ gistBufferingBuildInsert(buildstate, itup); diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index e53c63dc2b..46c1759b1e 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -31,6 +31,11 @@ static bool gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy); static bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy); +static int64 part_bits32_by2(uint32 x); +static int64 interleave_bits32(uint32 x, uint32 y); +static inline uint64 point_zorder_internal(Point *p); +static int gist_bbox_fastcmp(Datum x, Datum y, SortSupport ssup); + /* Minimum accepted ratio of split */ #define LIMIT_RATIO 0.3 @@ -1532,6 +1537,8 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } +/* Z-order routines */ +/* Interleave 32 bits with zeroes */ static int64 part_bits32_by2(uint32 x) { @@ -1546,12 +1553,14 @@ part_bits32_by2(uint32 x) return n; } +/* Compute Z-order for integers */ static int64 interleave_bits32(uint32 x, uint32 y) { return part_bits32_by2(x) | (part_bits32_by2(y) << 1); } +/* Compute Z-oder for point */ static inline uint64 point_zorder_internal(Point *p) { @@ -1559,16 +1568,20 @@ point_zorder_internal(Point *p) float f; uint32 i; } a,b; + if (isnan(a.f)) + a.i = INT32_MAX; + if (isnan(b.f)) + b.i = INT32_MAX; a.f = p->x; b.f = p->y; return interleave_bits32(a.i, b.i); } static int -gist_point_fastcmp(Datum x, Datum y, SortSupport ssup) +gist_bbox_fastcmp(Datum x, Datum y, SortSupport ssup) { - Point *p1 = DatumGetPointP(x); - Point *p2 = DatumGetPointP(y); + Point *p1 = &(DatumGetBoxP(x)->low); + Point *p2 = &(DatumGetBoxP(y)->low); uint64 z1 = point_zorder_internal(p1); uint64 z2 = point_zorder_internal(p2); @@ -1580,6 +1593,6 @@ gist_point_sortsupport(PG_FUNCTION_ARGS) { SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); - ssup->comparator = gist_point_fastcmp; + ssup->comparator = gist_bbox_fastcmp; PG_RETURN_VOID(); } diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 49df05653b..57fc479086 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -174,6 +174,7 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc, &IsNull); + if (IsNull) continue; @@ -576,6 +577,13 @@ gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf) { Datum compatt[INDEX_MAX_KEYS]; + return gistCompressValuesAndFormTuple(giststate, r, attdata, isnull, isleaf, compatt); +} + +IndexTuple +gistCompressValuesAndFormTuple(GISTSTATE *giststate, Relation r, + Datum attdata[], bool isnull[], bool isleaf, Datum compatt[]) +{ int i; IndexTuple res; @@ -746,14 +754,10 @@ gistpenalty(GISTSTATE *giststate, int attno, * Initialize a new index page */ void -GISTInitBuffer(Buffer b, uint32 f) +gistinitpage(Page page, uint32 f) { - GISTPageOpaque opaque; - Page page; - Size pageSize; - - pageSize = BufferGetPageSize(b); - page = BufferGetPage(b); + GISTPageOpaque opaque; + Size pageSize = BLCKSZ; PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); opaque = GistPageGetOpaque(page); @@ -764,6 +768,18 @@ GISTInitBuffer(Buffer b, uint32 f) opaque->gist_page_id = GIST_PAGE_ID; } +/* + * Initialize a new index buffer + */ +void +GISTInitBuffer(Buffer b, uint32 f) +{ + Page page; + + page = BufferGetPage(b); + gistinitpage(page, f); +} + /* * Verify that a freshly-read page looks sane. */ diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index dfc1a87a75..bc65b9c6bf 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -140,6 +140,10 @@ gistvalidate(Oid opclassoid) 5, 5, INTERNALOID, opcintype, INT2OID, OIDOID, INTERNALOID); break; + case GIST_SORTSUPPORT_PROC: + ok = check_amproc_signature(procform->amproc, VOIDOID, true, + 1, 1, INTERNALOID); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -259,7 +263,8 @@ gistvalidate(Oid opclassoid) (opclassgroup->functionset & (((uint64) 1) << i)) != 0) continue; /* got it */ if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC || - i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC) + i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC || + i == GIST_SORTSUPPORT_PROC) continue; /* optional methods */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 7b8e67899e..018901b654 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -1095,6 +1095,43 @@ tuplesort_begin_index_hash(Relation heapRel, return state; } +Tuplesortstate * +tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + SortSupport ssup, + int workMem, + SortCoordinate coordinate, + bool randomAccess) +{ + Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, + randomAccess); + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(state->sortcontext); + +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "begin index sort: workMem = %d, randomAccess = %c", + workMem, randomAccess ? 't' : 'f'); +#endif + + state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); + + state->comparetup = comparetup_index_btree; + state->copytup = copytup_index; + state->writetup = writetup_index; + state->readtup = readtup_index; + state->sortKeys = ssup; + + state->heapRel = heapRel; + state->indexRel = indexRel; + + MemoryContextSwitchTo(oldcontext); + + return state; +} + Tuplesortstate * tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 6902f4115b..eeea53a8f1 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -34,7 +34,8 @@ #define GIST_EQUAL_PROC 7 #define GIST_DISTANCE_PROC 8 #define GIST_FETCH_PROC 9 -#define GISTNProcs 9 +#define GIST_SORTSUPPORT_PROC 10 +#define GISTNProcs 10 /* * Page opaque data in a GiST index page. diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index f80694bf9a..b7b6ca13ac 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -486,12 +486,15 @@ extern IndexTuple gistgetadjusted(Relation r, GISTSTATE *giststate); extern IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf); +extern IndexTuple gistCompressValuesAndFormTuple(GISTSTATE *giststate, Relation r, + Datum attdata[], bool isnull[], bool isleaf, Datum compatt[]); extern OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate); extern void GISTInitBuffer(Buffer b, uint32 f); +extern void gistinitpage(Page page, uint32 f); extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull); diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 020b7413cc..1a52302f7e 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -409,6 +409,8 @@ amproc => 'gist_point_distance' }, { amprocfamily => 'gist/point_ops', amproclefttype => 'point', amprocrighttype => 'point', amprocnum => '9', amproc => 'gist_point_fetch' }, +{ amprocfamily => 'gist/point_ops', amproclefttype => 'point', + amprocrighttype => 'point', amprocnum => '10', amproc => 'gist_point_sortsupport' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', amproc => 'gist_box_consistent' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h index 4521de18e1..3f1ceac3d2 100644 --- a/src/include/utils/tuplesort.h +++ b/src/include/utils/tuplesort.h @@ -26,6 +26,7 @@ #include "fmgr.h" #include "storage/dsm.h" #include "utils/relcache.h" +#include "utils/sortsupport.h" /* @@ -209,6 +210,11 @@ extern Tuplesortstate *tuplesort_begin_index_hash(Relation heapRel, uint32 max_buckets, int workMem, SortCoordinate coordinate, bool randomAccess); +extern Tuplesortstate *tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + SortSupport ssup, + int workMem, SortCoordinate coordinate, + bool randomAccess); extern Tuplesortstate *tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, -- 2.17.1 =
v3-0001-Add-sort-support-for-point-gist_point_sortsupport.patch (application/octet-stream, 2.5K) download From 3f9d4f0b407339f9e40fc4ccc39d594f95ac84ca Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Sun, 25 Aug 2019 12:42:36 +0500 Subject: [PATCH 1/2] Add sort support for point gist_point_sortsupport --- src/backend/access/gist/gistproc.c | 53 ++++++++++++++++++++++++++++++ src/include/catalog/pg_proc.dat | 3 ++ 2 files changed, 56 insertions(+) diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 1826b51bbb..e53c63dc2b 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -24,6 +24,7 @@ #include "utils/builtins.h" #include "utils/float.h" #include "utils/geo_decls.h" +#include "utils/sortsupport.h" static bool gist_box_leaf_consistent(BOX *key, BOX *query, @@ -1530,3 +1531,55 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } + +static int64 +part_bits32_by2(uint32 x) +{ + uint64 n = x; + + n = (n | (n << 16)) & 0x0000FFFF0000FFFF; + n = (n | (n << 8)) & 0x00FF00FF00FF00FF; + n = (n | (n << 4)) & 0x0F0F0F0F0F0F0F0F; + n = (n | (n << 2)) & 0x3333333333333333; + n = (n | (n << 1)) & 0x5555555555555555; + + return n; +} + +static int64 +interleave_bits32(uint32 x, uint32 y) +{ + return part_bits32_by2(x) | (part_bits32_by2(y) << 1); +} + +static inline uint64 +point_zorder_internal(Point *p) +{ + union { + float f; + uint32 i; + } a,b; + a.f = p->x; + b.f = p->y; + return interleave_bits32(a.i, b.i); +} + +static int +gist_point_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + Point *p1 = DatumGetPointP(x); + Point *p2 = DatumGetPointP(y); + uint64 z1 = point_zorder_internal(p1); + uint64 z2 = point_zorder_internal(p2); + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +gist_point_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gist_point_fastcmp; + PG_RETURN_VOID(); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 87335248a0..de3cb2f1e3 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7849,6 +7849,9 @@ proname => 'gist_poly_distance', prorettype => 'float8', proargtypes => 'internal polygon int2 oid internal', prosrc => 'gist_poly_distance' }, +{ oid => '3429', descr => 'sort support', + proname => 'gist_point_sortsupport', prorettype => 'void', + proargtypes => 'internal', prosrc => 'gist_point_sortsupport' }, # GIN array support { oid => '2743', descr => 'GIN array support', -- 2.17.1 =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-12-01 02:06  Michael Paquier <michael@paquier.xyz>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Michael Paquier @ 2019-12-01 02:06 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Sun, Sep 08, 2019 at 01:54:35PM +0500, Andrey Borodin wrote:
> Here's V3 of the patch set.
> Changes:
> 1. Added some documentation of new sort support routines
> 2. Fixed bug with dirty pages
> 
> I did not add sort support procs to built-in boxes, circles and
> polys, since it may be not optimal index for them. However, for
> points Z-order is quite good as a defaul t.

The latest patch does not apply.  Could you send a rebase?  I have
moved the patch to next CF, waiting on author for now.
--
Michael

Attachments:

  signature.asc (application/pgp-signature, 832B) download

^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2019-12-30 06:43  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Michael Paquier <michael@paquier.xyz>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2019-12-30 06:43 UTC (permalink / raw)
  To: Michael Paquier <michael@paquier.xyz>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers



> 1 дек. 2019 г., в 7:06, Michael Paquier <michael@paquier.xyz> написал(а):
> 
> On Sun, Sep 08, 2019 at 01:54:35PM +0500, Andrey Borodin wrote:
>> Here's V3 of the patch set.
>> Changes:
>> 1. Added some documentation of new sort support routines
>> 2. Fixed bug with dirty pages
>> 
>> I did not add sort support procs to built-in boxes, circles and
>> polys, since it may be not optimal index for them. However, for
>> points Z-order is quite good as a defaul t.
> 
> The latest patch does not apply.  Could you send a rebase?  I have
> moved the patch to next CF, waiting on author for now.

Thanks, Michael!

PFA rebased patch.

Best regards, Andrey Borodin.


Attachments: download all (2 files, .tar.gz)

v4-0002-Implement-GiST-build-using-sort-support.patch (application/octet-stream, 21.0K) download From 1de0069c34abb3c323daf0da7e5346f15652ae6d Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Sun, 25 Aug 2019 14:18:31 +0500 Subject: [PATCH 2/2] Implement GiST build using sort support --- doc/src/sgml/gist.sgml | 51 +++++- src/backend/access/gist/gistbuild.c | 220 ++++++++++++++++++++++++- src/backend/access/gist/gistproc.c | 21 ++- src/backend/access/gist/gistutil.c | 30 +++- src/backend/access/gist/gistvalidate.c | 7 +- src/backend/utils/sort/tuplesort.c | 37 +++++ src/include/access/gist.h | 3 +- src/include/access/gist_private.h | 3 + src/include/catalog/pg_amproc.dat | 2 + src/include/utils/tuplesort.h | 6 + 10 files changed, 362 insertions(+), 18 deletions(-) diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml index a7eec1e949..0e67863a55 100644 --- a/doc/src/sgml/gist.sgml +++ b/doc/src/sgml/gist.sgml @@ -269,7 +269,7 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); <para> There are five methods that an index operator class for - <acronym>GiST</acronym> must provide, and four that are optional. + <acronym>GiST</acronym> must provide, and five that are optional. Correctness of the index is ensured by proper implementation of the <function>same</function>, <function>consistent</function> and <function>union</function> methods, while efficiency (size and speed) of the @@ -287,7 +287,8 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); if the operator class wishes to support ordered scans (nearest-neighbor searches). The optional ninth method <function>fetch</function> is needed if the operator class wishes to support index-only scans, except when the - <function>compress</function> method is omitted. + <function>compress</function> method is omitted. The <function>sortsupport</function> + method is also optional and is used diring fast GiST build. </para> <variablelist> @@ -939,6 +940,52 @@ my_fetch(PG_FUNCTION_ARGS) </listitem> </varlistentry> + <varlistentry> + <term><function>sortsupport</function></term> + <listitem> + <para> + Returns a comparator, suitable for fast GiST build. + </para> + + <para> + The <acronym>SQL</acronym> declaration of the function must look like this: + +<programlisting> +CREATE OR REPLACE FUNCTION my_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; +</programlisting> + + The argument is a pointer to a <structname>SortSupport</structname> struct. + </para> + + <para> + The matching code in the C module could then follow this skeleton: + +<programlisting> +PG_FUNCTION_INFO_V1(my_sortsupport); + +static int +my_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + /* esteblish order between x and y */ + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +my_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = my_fastcmp; + PG_RETURN_VOID(); +} +</programlisting> + </para> + </listitem> + </varlistentry> </variablelist> <para> diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index 739846a257..25ede54ae8 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -28,6 +28,8 @@ #include "storage/smgr.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/regproc.h" +#include "utils/tuplesort.h" /* Step of index tuples for check whether to switch to buffering build mode */ #define BUFFERING_MODE_SWITCH_CHECK_STEP 256 @@ -53,6 +55,15 @@ typedef enum GIST_BUFFERING_ACTIVE /* in buffering build mode */ } GistBufferingMode; +/* + * Status record for spooling/sorting phase. + */ +typedef struct +{ + Tuplesortstate *sortstate; /* state data for tuplesort.c */ + Relation index; +} GSpool; + /* Working state for gistbuild and its callback */ typedef struct { @@ -74,6 +85,7 @@ typedef struct HTAB *parentMap; GistBufferingMode bufferingMode; + GSpool *spool; } GISTBuildState; /* prototypes for private functions */ @@ -107,6 +119,174 @@ static void gistMemorizeParent(GISTBuildState *buildstate, BlockNumber child, static void gistMemorizeAllDownlinks(GISTBuildState *buildstate, Buffer parent); static BlockNumber gistGetParent(GISTBuildState *buildstate, BlockNumber child); +static GSpool *gist_spoolinit(Relation heap, Relation index, SortSupport ssup); +static void gist_spool(GSpool *gspool, ItemPointer self, Datum *values, bool *isnull); +static void gist_indexsortbuild(GISTBuildState *state); +static void gist_indexsortbuild_flush(Relation rel, Page page, BlockNumber blockno, bool isNew); + +/* + * create and initialize a spool structure to sort tuples + */ +static GSpool * +gist_spoolinit(Relation heap, Relation index, SortSupport ssup) +{ + GSpool *gspool = (GSpool *) palloc0(sizeof(GSpool)); + + gspool->index = index; + + /* + * We size the sort area as maintenance_work_mem rather than work_mem to + * speed index creation. This should be OK since a single backend can't + * run multiple index creations in parallel. + */ + gspool->sortstate = tuplesort_begin_index_gist(heap, + index, + ssup, + maintenance_work_mem, + NULL, + false); + + return gspool; +} + +/* + * Flush page contents to actual page at blockno + * We have expected block number, because GiST build relies on that pages + * will be allocated in continous segments. This simplifies allocation + * logic. + */ +static void +gist_indexsortbuild_flush(Relation rel, Page page, BlockNumber blockno, bool isNew) +{ + OffsetNumber i, + maxoff; + Page newpage; + + Buffer buffer = ReadBuffer(rel, isNew ? P_NEW : blockno); + GISTInitBuffer(buffer, GistPageIsLeaf(page) ? F_LEAF : 0); + /* If the page is new - check that it was allocated correctly */ + Assert(BufferGetBlockNumber(buffer) == blockno); + + LockBuffer(buffer, GIST_EXCLUSIVE); + newpage = BufferGetPage(buffer); + + maxoff = PageGetMaxOffsetNumber(page); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + ItemId itemid = PageGetItemId(page, i); + IndexTuple tuple = (IndexTuple) PageGetItem(page, itemid); + gistfillbuffer(newpage, &tuple, + 1, InvalidOffsetNumber); + } + MarkBufferDirty(buffer); + UnlockReleaseBuffer(buffer); +} + +/* + * given a spool loaded by successive calls to _h_spool, + * create an entire index. + */ +static void +gist_indexsortbuild(GISTBuildState *state) +{ + GSpool *gspool = state->spool; + /* + * Build constructs GiST by levels. Level is always allocated + * sequentially from level_start until level_end. + */ + BlockNumber level_start = GIST_ROOT_BLKNO + 1; + BlockNumber level_end = level_start; + BlockNumber prev_level_start; + IndexTuple itup; + /* + * We keep one page in memory for the special case + * When layer will have only on page - we will place it + * to ROOT_BLOCK_NO + */ + Page page = palloc(BLCKSZ); + gistinitpage(page, F_LEAF); + + tuplesort_performsort(gspool->sortstate); + + /* Create a first layer of leaf pages */ + while ((itup = tuplesort_getindextuple(gspool->sortstate, true)) != NULL) + { + if (PageGetFreeSpace(page) >= IndexTupleSize(itup) + sizeof(ItemIdData) + state->freespace) + { + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); + } + else + { + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, F_LEAF); + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); + } + } + + /* Construct internal levels */ + do + { + /* If previous level had only one page - that page is a root */ + if (level_start == level_end) + { + gist_indexsortbuild_flush(state->indexrel, page, GIST_ROOT_BLKNO, + false); + return; + } + + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, 0); + + prev_level_start = level_start; + level_start = level_end; + + for (BlockNumber i = prev_level_start; i < level_start; i++) + { + /* For each page on previous level we form one tuple */ + Buffer lower_buffer = ReadBuffer(state->indexrel, i); + Page lower_page; + IndexTuple union_tuple; + MemoryContext oldCtx = MemoryContextSwitchTo(state->giststate->tempCxt); + int vect_len; + LockBuffer(lower_buffer, GIST_SHARE); + lower_page = BufferGetPage(lower_buffer); + + IndexTuple *itvec = gistextractpage(lower_page, &vect_len); + union_tuple = gistunion(state->indexrel, itvec, vect_len, + state->giststate); + ItemPointerSetBlockNumber(&(union_tuple->t_tid), i); + + if (PageGetFreeSpace(page) >= IndexTupleSize(union_tuple) + sizeof(ItemIdData) + state->freespace) + { + gistfillbuffer(page, &union_tuple, 1, InvalidOffsetNumber); + } + else + { + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, 0); + gistfillbuffer(page, &union_tuple, 1, InvalidOffsetNumber); + } + + UnlockReleaseBuffer(lower_buffer); + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(state->giststate->tempCxt); + } + } while (true); +} + +/* + * spool an index entry into the sort file. + */ +static void +gist_spool(GSpool *gspool, ItemPointer self, Datum *values, bool *isnull) +{ + tuplesort_putindextuplevalues(gspool->sortstate, gspool->index, + self, values, isnull); +} + /* * Main entry point to GiST index build. Initially calls insert over and over, * but switches to more efficient buffering build algorithm after a certain @@ -121,11 +301,15 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) Buffer buffer; Page page; MemoryContext oldcxt = CurrentMemoryContext; - int fillfactor; + int fillfactor, i; + Oid SortSupportFnOids[INDEX_MAX_KEYS]; + bool hasallsortsupports = true; + int keyscount = IndexRelationGetNumberOfKeyAttributes(index); buildstate.indexrel = index; buildstate.heaprel = heap; + buildstate.spool = NULL; if (index->rd_options) { /* Get buffering mode from the options string */ @@ -152,6 +336,24 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) /* Calculate target amount of free space to leave on pages */ buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100; + for (i = 0; i < keyscount; i++) + { + SortSupportFnOids[i] = index_getprocid(index, i + 1, GIST_SORTSUPPORT_PROC); + if (!OidIsValid(SortSupportFnOids[i])) + { + hasallsortsupports = false; + break; + } + } + if (hasallsortsupports) + { + SortSupport sort = palloc0(sizeof(SortSupportData) * keyscount); + for (i = 0; i < keyscount; i++) + OidFunctionCall1(SortSupportFnOids[i], PointerGetDatum(sort + i)); + buildstate.bufferingMode = GIST_BUFFERING_DISABLED; + buildstate.spool = gist_spoolinit(heap, index, sort); + } + /* * We expect to be called exactly once for any index relation. If that's * not the case, big trouble's what we have. @@ -208,6 +410,12 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) gistFreeBuildBuffers(buildstate.gfbb); } + if (buildstate.spool) + { + gist_indexsortbuild(&buildstate); + tuplesort_end(buildstate.spool->sortstate); + } + /* okay, all heap tuples are indexed */ MemoryContextSwitchTo(oldcxt); MemoryContextDelete(buildstate.giststate->tempCxt); @@ -449,14 +657,20 @@ gistBuildCallback(Relation index, GISTBuildState *buildstate = (GISTBuildState *) state; IndexTuple itup; MemoryContext oldCtx; + Datum compressed_values[INDEX_MAX_KEYS]; oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); /* form an index tuple and point it at the heap tuple */ - itup = gistFormTuple(buildstate->giststate, index, values, isnull, true); + itup = gistCompressValuesAndFormTuple(buildstate->giststate, index, values, isnull, true, compressed_values); itup->t_tid = *tid; - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) + if (buildstate->spool) + { + if (tupleIsAlive) + gist_spool(buildstate->spool, (ItemPointer)itup, compressed_values, isnull); + } + else if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) { /* We have buffers, so use them. */ gistBufferingBuildInsert(buildstate, itup); diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 7d50550525..056fa10080 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -31,6 +31,11 @@ static bool gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy); static bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy); +static int64 part_bits32_by2(uint32 x); +static int64 interleave_bits32(uint32 x, uint32 y); +static inline uint64 point_zorder_internal(Point *p); +static int gist_bbox_fastcmp(Datum x, Datum y, SortSupport ssup); + /* Minimum accepted ratio of split */ #define LIMIT_RATIO 0.3 @@ -1546,6 +1551,8 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } +/* Z-order routines */ +/* Interleave 32 bits with zeroes */ static int64 part_bits32_by2(uint32 x) { @@ -1560,12 +1567,14 @@ part_bits32_by2(uint32 x) return n; } +/* Compute Z-order for integers */ static int64 interleave_bits32(uint32 x, uint32 y) { return part_bits32_by2(x) | (part_bits32_by2(y) << 1); } +/* Compute Z-oder for point */ static inline uint64 point_zorder_internal(Point *p) { @@ -1573,16 +1582,20 @@ point_zorder_internal(Point *p) float f; uint32 i; } a,b; + if (isnan(a.f)) + a.i = INT32_MAX; + if (isnan(b.f)) + b.i = INT32_MAX; a.f = p->x; b.f = p->y; return interleave_bits32(a.i, b.i); } static int -gist_point_fastcmp(Datum x, Datum y, SortSupport ssup) +gist_bbox_fastcmp(Datum x, Datum y, SortSupport ssup) { - Point *p1 = DatumGetPointP(x); - Point *p2 = DatumGetPointP(y); + Point *p1 = &(DatumGetBoxP(x)->low); + Point *p2 = &(DatumGetBoxP(y)->low); uint64 z1 = point_zorder_internal(p1); uint64 z2 = point_zorder_internal(p2); @@ -1594,6 +1607,6 @@ gist_point_sortsupport(PG_FUNCTION_ARGS) { SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); - ssup->comparator = gist_point_fastcmp; + ssup->comparator = gist_bbox_fastcmp; PG_RETURN_VOID(); } diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 553a6d67b1..b3a675df11 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -173,6 +173,7 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc, &IsNull); + if (IsNull) continue; @@ -575,6 +576,13 @@ gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf) { Datum compatt[INDEX_MAX_KEYS]; + return gistCompressValuesAndFormTuple(giststate, r, attdata, isnull, isleaf, compatt); +} + +IndexTuple +gistCompressValuesAndFormTuple(GISTSTATE *giststate, Relation r, + Datum attdata[], bool isnull[], bool isleaf, Datum compatt[]) +{ int i; IndexTuple res; @@ -745,14 +753,10 @@ gistpenalty(GISTSTATE *giststate, int attno, * Initialize a new index page */ void -GISTInitBuffer(Buffer b, uint32 f) +gistinitpage(Page page, uint32 f) { - GISTPageOpaque opaque; - Page page; - Size pageSize; - - pageSize = BufferGetPageSize(b); - page = BufferGetPage(b); + GISTPageOpaque opaque; + Size pageSize = BLCKSZ; PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); opaque = GistPageGetOpaque(page); @@ -763,6 +767,18 @@ GISTInitBuffer(Buffer b, uint32 f) opaque->gist_page_id = GIST_PAGE_ID; } +/* + * Initialize a new index buffer + */ +void +GISTInitBuffer(Buffer b, uint32 f) +{ + Page page; + + page = BufferGetPage(b); + gistinitpage(page, f); +} + /* * Verify that a freshly-read page looks sane. */ diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index dfc1a87a75..bc65b9c6bf 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -140,6 +140,10 @@ gistvalidate(Oid opclassoid) 5, 5, INTERNALOID, opcintype, INT2OID, OIDOID, INTERNALOID); break; + case GIST_SORTSUPPORT_PROC: + ok = check_amproc_signature(procform->amproc, VOIDOID, true, + 1, 1, INTERNALOID); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -259,7 +263,8 @@ gistvalidate(Oid opclassoid) (opclassgroup->functionset & (((uint64) 1) << i)) != 0) continue; /* got it */ if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC || - i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC) + i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC || + i == GIST_SORTSUPPORT_PROC) continue; /* optional methods */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 7947d2bca0..d1231a80e5 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -1095,6 +1095,43 @@ tuplesort_begin_index_hash(Relation heapRel, return state; } +Tuplesortstate * +tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + SortSupport ssup, + int workMem, + SortCoordinate coordinate, + bool randomAccess) +{ + Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, + randomAccess); + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(state->sortcontext); + +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "begin index sort: workMem = %d, randomAccess = %c", + workMem, randomAccess ? 't' : 'f'); +#endif + + state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); + + state->comparetup = comparetup_index_btree; + state->copytup = copytup_index; + state->writetup = writetup_index; + state->readtup = readtup_index; + state->sortKeys = ssup; + + state->heapRel = heapRel; + state->indexRel = indexRel; + + MemoryContextSwitchTo(oldcontext); + + return state; +} + Tuplesortstate * tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 8292956cc0..282c24a19a 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -35,7 +35,8 @@ #define GIST_EQUAL_PROC 7 #define GIST_DISTANCE_PROC 8 #define GIST_FETCH_PROC 9 -#define GISTNProcs 9 +#define GIST_SORTSUPPORT_PROC 10 +#define GISTNProcs 10 /* * Page opaque data in a GiST index page. diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index a409975db1..d31278daf0 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -495,12 +495,15 @@ extern IndexTuple gistgetadjusted(Relation r, GISTSTATE *giststate); extern IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf); +extern IndexTuple gistCompressValuesAndFormTuple(GISTSTATE *giststate, Relation r, + Datum attdata[], bool isnull[], bool isleaf, Datum compatt[]); extern OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate); extern void GISTInitBuffer(Buffer b, uint32 f); +extern void gistinitpage(Page page, uint32 f); extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull); diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 5e705019b4..42eced0cd7 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -412,6 +412,8 @@ amproc => 'gist_point_distance' }, { amprocfamily => 'gist/point_ops', amproclefttype => 'point', amprocrighttype => 'point', amprocnum => '9', amproc => 'gist_point_fetch' }, +{ amprocfamily => 'gist/point_ops', amproclefttype => 'point', + amprocrighttype => 'point', amprocnum => '10', amproc => 'gist_point_sortsupport' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', amproc => 'gist_box_consistent' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h index d774bc1152..d404fe4c46 100644 --- a/src/include/utils/tuplesort.h +++ b/src/include/utils/tuplesort.h @@ -25,6 +25,7 @@ #include "executor/tuptable.h" #include "storage/dsm.h" #include "utils/relcache.h" +#include "utils/sortsupport.h" /* @@ -208,6 +209,11 @@ extern Tuplesortstate *tuplesort_begin_index_hash(Relation heapRel, uint32 max_buckets, int workMem, SortCoordinate coordinate, bool randomAccess); +extern Tuplesortstate *tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + SortSupport ssup, + int workMem, SortCoordinate coordinate, + bool randomAccess); extern Tuplesortstate *tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, -- 2.20.1 =
v4-0001-Add-sort-support-for-point-gist_point_sortsupport.patch (application/octet-stream, 2.5K) download From b5aca8b5f1226eda85ad0c003d33bcf673765771 Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Sun, 25 Aug 2019 12:42:36 +0500 Subject: [PATCH 1/2] Add sort support for point gist_point_sortsupport --- src/backend/access/gist/gistproc.c | 53 ++++++++++++++++++++++++++++++ src/include/catalog/pg_proc.dat | 3 ++ 2 files changed, 56 insertions(+) diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 118dd9653f..7d50550525 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -24,6 +24,7 @@ #include "utils/builtins.h" #include "utils/float.h" #include "utils/geo_decls.h" +#include "utils/sortsupport.h" static bool gist_box_leaf_consistent(BOX *key, BOX *query, @@ -1544,3 +1545,55 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } + +static int64 +part_bits32_by2(uint32 x) +{ + uint64 n = x; + + n = (n | (n << 16)) & 0x0000FFFF0000FFFF; + n = (n | (n << 8)) & 0x00FF00FF00FF00FF; + n = (n | (n << 4)) & 0x0F0F0F0F0F0F0F0F; + n = (n | (n << 2)) & 0x3333333333333333; + n = (n | (n << 1)) & 0x5555555555555555; + + return n; +} + +static int64 +interleave_bits32(uint32 x, uint32 y) +{ + return part_bits32_by2(x) | (part_bits32_by2(y) << 1); +} + +static inline uint64 +point_zorder_internal(Point *p) +{ + union { + float f; + uint32 i; + } a,b; + a.f = p->x; + b.f = p->y; + return interleave_bits32(a.i, b.i); +} + +static int +gist_point_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + Point *p1 = DatumGetPointP(x); + Point *p2 = DatumGetPointP(y); + uint64 z1 = point_zorder_internal(p1); + uint64 z2 = point_zorder_internal(p2); + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +gist_point_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gist_point_fastcmp; + PG_RETURN_VOID(); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index ac8f64b219..ab7eeb2986 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7893,6 +7893,9 @@ proname => 'gist_poly_distance', prorettype => 'float8', proargtypes => 'internal polygon int2 oid internal', prosrc => 'gist_poly_distance' }, +{ oid => '3435', descr => 'sort support', + proname => 'gist_point_sortsupport', prorettype => 'void', + proargtypes => 'internal', prosrc => 'gist_point_sortsupport' }, # GIN array support { oid => '2743', descr => 'GIN array support', -- 2.20.1 =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-02-19 07:00  Thomas Munro <thomas.munro@gmail.com>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Thomas Munro @ 2020-02-19 07:00 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Mon, Dec 30, 2019 at 7:43 PM Andrey Borodin <x4mmm@yandex-team.ru> wrote:
> PFA rebased patch.

Hi Andrey,

This looks really interesting, and I am sure there are a lot of GIS
people who would love to see dramatically faster and smaller indexes
in PG13.  I don't know enough to comment on the details, but here are
some superficial comments:

+   method is also optional and is used diring fast GiST build.

-> during

+    /* esteblish order between x and y */

-> establish

+/* Compute Z-oder for point */
 static inline uint64
 point_zorder_internal(Point *p)

-> order

Could this function please have a comment that explains why it works?
I mean, just a breadcrumb... the name of the technique or something...
so that uninitiated hackers can google their way to a clue (is it
"Morton encoding"?)

MSVC says:

src/backend/access/gist/gistproc.c(1582): error C2065: 'INT32_MAX' :
undeclared identifier

GCC says:

gistbuild.c: In function ‘gist_indexsortbuild’:
gistbuild.c:256:4: error: ISO C90 forbids mixed declarations and code
[-Werror=declaration-after-statement]
    IndexTuple *itvec = gistextractpage(lower_page, &vect_len);
    ^





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-02-19 21:14  Thomas Munro <thomas.munro@gmail.com>
  parent: Thomas Munro <thomas.munro@gmail.com>
  0 siblings, 1 reply; 111+ messages in thread

From: Thomas Munro @ 2020-02-19 21:14 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Wed, Feb 19, 2020 at 8:00 PM Thomas Munro <thomas.munro@gmail.com> wrote:
> Could this function please have a comment that explains why it works?
> I mean, just a breadcrumb... the name of the technique or something...
> so that uninitiated hackers can google their way to a clue (is it
> "Morton encoding"?)

Ok I think I get it now after doing some homework.

1.  We expect floats to be in IEEE format, and the sort order of IEEE
floats is mostly correlated to the binary sort order of the bits
reinterpreted as an int.  It isn't in some special cases, but for this
use case we don't really care about that, we're just trying to
encourage locality.
2.  We generate a Morton code that interleaves the bits of N integers
to produce a single integer that preserves locality: things that were
close in the N dimensional space are close in the resulting integer.

Cool.

+static int
+my_fastcmp(Datum x, Datum y, SortSupport ssup)
+{
+    /* esteblish order between x and y */
+
+    return z1 == z2 ? 0 : z1 > z2 ? 1 : -1;
+}

This example code from the documentation looks wrong, probably missing
eg int64 z1 = DatumGetInt64(x).





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-02-23 20:58  Thomas Munro <thomas.munro@gmail.com>
  parent: Thomas Munro <thomas.munro@gmail.com>
  0 siblings, 1 reply; 111+ messages in thread

From: Thomas Munro @ 2020-02-23 20:58 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Thu, Feb 20, 2020 at 10:14 AM Thomas Munro <thomas.munro@gmail.com> wrote:
> 1.  We expect floats to be in IEEE format, and the sort order of IEEE
> floats is mostly correlated to the binary sort order of the bits
> reinterpreted as an int.  It isn't in some special cases, but for this
> use case we don't really care about that, we're just trying to
> encourage locality.

I suppose there is a big jump in integer value (whether signed or
unsigned) as you cross from positive to negative floats, and then the
sort order is reversed.  I have no idea if either of those things is a
problem worth fixing.  That made me wonder if there might also be an
endianness problem.  It seems from some quick googling that all
current architectures have integers and floats of the same endianness.
Apparently this wasn't always the case, and some ARMs have a weird
half-flipped arrangement for 64 bit floats, but not 32 bit floats as
you are using here.





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-02-24 08:50  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Thomas Munro <thomas.munro@gmail.com>
  0 siblings, 3 replies; 111+ messages in thread

From: Andrey M. Borodin @ 2020-02-24 08:50 UTC (permalink / raw)
  To: Thomas Munro <thomas.munro@gmail.com>; +Cc: Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

Hi Thomas!

Thanks for looking into this! I’ll fix your notices asap.

> On 24 февр. 2020 г., at 01:58, Thomas Munro <thomas.munro@gmail.com> wrote:
> 
> On Thu, Feb 20, 2020 at 10:14 AM Thomas Munro <thomas.munro@gmail.com> wrote:
>> 1.  We expect floats to be in IEEE format, and the sort order of IEEE
>> floats is mostly correlated to the binary sort order of the bits
>> reinterpreted as an int.  It isn't in some special cases, but for this
>> use case we don't really care about that, we're just trying to
>> encourage locality.
> 
> I suppose there is a big jump in integer value (whether signed or
> unsigned) as you cross from positive to negative floats, and then the
> sort order is reversed.  I have no idea if either of those things is a
> problem worth fixing.  That made me wonder if there might also be an
> endianness problem.  It seems from some quick googling that all
> current architectures have integers and floats of the same endianness.
> Apparently this wasn't always the case, and some ARMs have a weird
> half-flipped arrangement for 64 bit floats, but not 32 bit floats as
> you are using here.

Yes, this leap is a problem for point as generic data type. And I do not know
how to fix it. It can cause inefficient Index Scans when searching near (0,0) and query
window touches simultaneously all quadrants (4x slower).
But everything will be just fine when all data is in 2nd quadrant.

Actually, we do not need to add this hacky code to core: we can provide colum-wise
ordering or something similar as an example.
This feature is aimed at PostGIS and they already possess bit tricks tricks [0].
I’ve taken this union code from PostGIS.

Thanks!

Best regards, Andrey Borodin.


[0] https://github.com/postgis/postgis/blob/master/postgis/gserialized_gist_nd.c#L1150




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-02-29 12:13  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  2 siblings, 1 reply; 111+ messages in thread

From: Andrey M. Borodin @ 2020-02-29 12:13 UTC (permalink / raw)
  To: Thomas Munro <thomas.munro@gmail.com>; +Cc: Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

Hi!

> On 24 февр. 2020 г., at 13:50, Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:
> 
> Hi Thomas!
> 
> Thanks for looking into this! I’ll fix your notices asap.

PFA v5.
Thomas, I've used your wording almost exactly with explanation how
point_zorder_internal() works. It has more explanation power than my attempts
to compose good comment.

There is one design decision that worries me most:
should we use opclass function or index option to provide this sorting information?
It is needed only during index creation, actually. And having extra i-class only for fast build
seems excessive.
I think we can provide both ways and let opclass developers decide?

Thanks!

Best regards, Andrey Borodin.

Attachments: download all (2 files, .tar.gz)

v5-0001-Add-sort-support-for-point-gist_point_sortsupport.patch (application/octet-stream, 2.5K) download From fc49f7501f167cc714c0e24e3fb99e8138787f23 Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Sun, 25 Aug 2019 12:42:36 +0500 Subject: [PATCH 1/2] Add sort support for point gist_point_sortsupport --- src/backend/access/gist/gistproc.c | 53 ++++++++++++++++++++++++++++++ src/include/catalog/pg_proc.dat | 3 ++ 2 files changed, 56 insertions(+) diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 9ace64c3c4..a28ca4a0c4 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -24,6 +24,7 @@ #include "utils/builtins.h" #include "utils/float.h" #include "utils/geo_decls.h" +#include "utils/sortsupport.h" static bool gist_box_leaf_consistent(BOX *key, BOX *query, @@ -1540,3 +1541,55 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } + +static int64 +part_bits32_by2(uint32 x) +{ + uint64 n = x; + + n = (n | (n << 16)) & 0x0000FFFF0000FFFF; + n = (n | (n << 8)) & 0x00FF00FF00FF00FF; + n = (n | (n << 4)) & 0x0F0F0F0F0F0F0F0F; + n = (n | (n << 2)) & 0x3333333333333333; + n = (n | (n << 1)) & 0x5555555555555555; + + return n; +} + +static int64 +interleave_bits32(uint32 x, uint32 y) +{ + return part_bits32_by2(x) | (part_bits32_by2(y) << 1); +} + +static inline uint64 +point_zorder_internal(Point *p) +{ + union { + float f; + uint32 i; + } a,b; + a.f = p->x; + b.f = p->y; + return interleave_bits32(a.i, b.i); +} + +static int +gist_point_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + Point *p1 = DatumGetPointP(x); + Point *p2 = DatumGetPointP(y); + uint64 z1 = point_zorder_internal(p1); + uint64 z2 = point_zorder_internal(p2); + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +gist_point_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gist_point_fastcmp; + PG_RETURN_VOID(); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 07a86c7b7b..999e037f78 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7931,6 +7931,9 @@ proname => 'gist_poly_distance', prorettype => 'float8', proargtypes => 'internal polygon int2 oid internal', prosrc => 'gist_poly_distance' }, +{ oid => '3435', descr => 'sort support', + proname => 'gist_point_sortsupport', prorettype => 'void', + proargtypes => 'internal', prosrc => 'gist_point_sortsupport' }, # GIN array support { oid => '2743', descr => 'GIN array support', -- 2.21.1 (Apple Git-122.3) =
v5-0002-Implement-GiST-build-using-sort-support.patch (application/octet-stream, 22.1K) download From 3f9f50a4498c6e0029d5f190b1f8c24cf9cce80b Mon Sep 17 00:00:00 2001 From: Andrey <amborodin@acm.org> Date: Sun, 25 Aug 2019 14:18:31 +0500 Subject: [PATCH 2/2] Implement GiST build using sort support --- doc/src/sgml/gist.sgml | 54 +++++- src/backend/access/gist/gistbuild.c | 221 ++++++++++++++++++++++++- src/backend/access/gist/gistproc.c | 41 ++++- src/backend/access/gist/gistutil.c | 30 +++- src/backend/access/gist/gistvalidate.c | 7 +- src/backend/utils/sort/tuplesort.c | 37 +++++ src/include/access/gist.h | 3 +- src/include/access/gist_private.h | 3 + src/include/catalog/pg_amproc.dat | 2 + src/include/utils/tuplesort.h | 6 + 10 files changed, 386 insertions(+), 18 deletions(-) diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml index a7eec1e949..4bced32ba9 100644 --- a/doc/src/sgml/gist.sgml +++ b/doc/src/sgml/gist.sgml @@ -269,7 +269,7 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); <para> There are five methods that an index operator class for - <acronym>GiST</acronym> must provide, and four that are optional. + <acronym>GiST</acronym> must provide, and five that are optional. Correctness of the index is ensured by proper implementation of the <function>same</function>, <function>consistent</function> and <function>union</function> methods, while efficiency (size and speed) of the @@ -287,7 +287,8 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); if the operator class wishes to support ordered scans (nearest-neighbor searches). The optional ninth method <function>fetch</function> is needed if the operator class wishes to support index-only scans, except when the - <function>compress</function> method is omitted. + <function>compress</function> method is omitted. The <function>sortsupport</function> + method is also optional and is used diring fast GiST build. </para> <variablelist> @@ -939,6 +940,55 @@ my_fetch(PG_FUNCTION_ARGS) </listitem> </varlistentry> + <varlistentry> + <term><function>sortsupport</function></term> + <listitem> + <para> + Returns a comparator, suitable for fast GiST build. + </para> + + <para> + The <acronym>SQL</acronym> declaration of the function must look like this: + +<programlisting> +CREATE OR REPLACE FUNCTION my_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; +</programlisting> + + The argument is a pointer to a <structname>SortSupport</structname> struct. + </para> + + <para> + The matching code in the C module could then follow this skeleton: + +<programlisting> +PG_FUNCTION_INFO_V1(my_sortsupport); + +static int +my_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + /* establish order between x and y by computing some sorting value z */ + + int z1 = ComputeSpatialCode(x); + int z2 = ComputeSpatialCode(y); + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +my_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = my_fastcmp; + PG_RETURN_VOID(); +} +</programlisting> + </para> + </listitem> + </varlistentry> </variablelist> <para> diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index 671b5e9186..0df03c8c36 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -28,6 +28,8 @@ #include "storage/smgr.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/regproc.h" +#include "utils/tuplesort.h" /* Step of index tuples for check whether to switch to buffering build mode */ #define BUFFERING_MODE_SWITCH_CHECK_STEP 256 @@ -53,6 +55,15 @@ typedef enum GIST_BUFFERING_ACTIVE /* in buffering build mode */ } GistBufferingMode; +/* + * Status record for spooling/sorting phase. + */ +typedef struct +{ + Tuplesortstate *sortstate; /* state data for tuplesort.c */ + Relation index; +} GSpool; + /* Working state for gistbuild and its callback */ typedef struct { @@ -74,6 +85,7 @@ typedef struct HTAB *parentMap; GistBufferingMode bufferingMode; + GSpool *spool; } GISTBuildState; /* prototypes for private functions */ @@ -107,6 +119,175 @@ static void gistMemorizeParent(GISTBuildState *buildstate, BlockNumber child, static void gistMemorizeAllDownlinks(GISTBuildState *buildstate, Buffer parent); static BlockNumber gistGetParent(GISTBuildState *buildstate, BlockNumber child); +static GSpool *gist_spoolinit(Relation heap, Relation index, SortSupport ssup); +static void gist_spool(GSpool *gspool, ItemPointer self, Datum *values, bool *isnull); +static void gist_indexsortbuild(GISTBuildState *state); +static void gist_indexsortbuild_flush(Relation rel, Page page, BlockNumber blockno, bool isNew); + +/* + * create and initialize a spool structure to sort tuples + */ +static GSpool * +gist_spoolinit(Relation heap, Relation index, SortSupport ssup) +{ + GSpool *gspool = (GSpool *) palloc0(sizeof(GSpool)); + + gspool->index = index; + + /* + * We size the sort area as maintenance_work_mem rather than work_mem to + * speed index creation. This should be OK since a single backend can't + * run multiple index creations in parallel. + */ + gspool->sortstate = tuplesort_begin_index_gist(heap, + index, + ssup, + maintenance_work_mem, + NULL, + false); + + return gspool; +} + +/* + * Flush page contents to actual page at blockno + * We have expected block number, because GiST build relies on that pages + * will be allocated in continous segments. This simplifies allocation + * logic. + */ +static void +gist_indexsortbuild_flush(Relation rel, Page page, BlockNumber blockno, bool isNew) +{ + OffsetNumber i, + maxoff; + Page newpage; + + Buffer buffer = ReadBuffer(rel, isNew ? P_NEW : blockno); + GISTInitBuffer(buffer, GistPageIsLeaf(page) ? F_LEAF : 0); + /* If the page is new - check that it was allocated correctly */ + Assert(BufferGetBlockNumber(buffer) == blockno); + + LockBuffer(buffer, GIST_EXCLUSIVE); + newpage = BufferGetPage(buffer); + + maxoff = PageGetMaxOffsetNumber(page); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + ItemId itemid = PageGetItemId(page, i); + IndexTuple tuple = (IndexTuple) PageGetItem(page, itemid); + gistfillbuffer(newpage, &tuple, + 1, InvalidOffsetNumber); + } + MarkBufferDirty(buffer); + UnlockReleaseBuffer(buffer); +} + +/* + * given a spool loaded by successive calls to _h_spool, + * create an entire index. + */ +static void +gist_indexsortbuild(GISTBuildState *state) +{ + GSpool *gspool = state->spool; + /* + * Build constructs GiST by levels. Level is always allocated + * sequentially from level_start until level_end. + */ + BlockNumber level_start = GIST_ROOT_BLKNO + 1; + BlockNumber level_end = level_start; + BlockNumber prev_level_start; + IndexTuple itup; + /* + * We keep one page in memory for the special case + * When layer will have only on page - we will place it + * to ROOT_BLOCK_NO + */ + Page page = palloc(BLCKSZ); + gistinitpage(page, F_LEAF); + + tuplesort_performsort(gspool->sortstate); + + /* Create a first layer of leaf pages */ + while ((itup = tuplesort_getindextuple(gspool->sortstate, true)) != NULL) + { + if (PageGetFreeSpace(page) >= IndexTupleSize(itup) + sizeof(ItemIdData) + state->freespace) + { + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); + } + else + { + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, F_LEAF); + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); + } + } + + /* Construct internal levels */ + do + { + /* If previous level had only one page - that page is a root */ + if (level_start == level_end) + { + gist_indexsortbuild_flush(state->indexrel, page, GIST_ROOT_BLKNO, + false); + return; + } + + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, 0); + + prev_level_start = level_start; + level_start = level_end; + + for (BlockNumber i = prev_level_start; i < level_start; i++) + { + /* For each page on previous level we form one tuple */ + Buffer lower_buffer = ReadBuffer(state->indexrel, i); + Page lower_page; + IndexTuple union_tuple; + IndexTuple *itvec; + MemoryContext oldCtx = MemoryContextSwitchTo(state->giststate->tempCxt); + int vect_len; + LockBuffer(lower_buffer, GIST_SHARE); + lower_page = BufferGetPage(lower_buffer); + + itvec = gistextractpage(lower_page, &vect_len); + union_tuple = gistunion(state->indexrel, itvec, vect_len, + state->giststate); + ItemPointerSetBlockNumber(&(union_tuple->t_tid), i); + + if (PageGetFreeSpace(page) >= IndexTupleSize(union_tuple) + sizeof(ItemIdData) + state->freespace) + { + gistfillbuffer(page, &union_tuple, 1, InvalidOffsetNumber); + } + else + { + gist_indexsortbuild_flush(state->indexrel, page, level_end, true); + level_end++; + gistinitpage(page, 0); + gistfillbuffer(page, &union_tuple, 1, InvalidOffsetNumber); + } + + UnlockReleaseBuffer(lower_buffer); + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(state->giststate->tempCxt); + } + } while (true); +} + +/* + * spool an index entry into the sort file. + */ +static void +gist_spool(GSpool *gspool, ItemPointer self, Datum *values, bool *isnull) +{ + tuplesort_putindextuplevalues(gspool->sortstate, gspool->index, + self, values, isnull); +} + /* * Main entry point to GiST index build. Initially calls insert over and over, * but switches to more efficient buffering build algorithm after a certain @@ -121,11 +302,15 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) Buffer buffer; Page page; MemoryContext oldcxt = CurrentMemoryContext; - int fillfactor; + int fillfactor, i; + Oid SortSupportFnOids[INDEX_MAX_KEYS]; + bool hasallsortsupports = true; + int keyscount = IndexRelationGetNumberOfKeyAttributes(index); buildstate.indexrel = index; buildstate.heaprel = heap; + buildstate.spool = NULL; if (index->rd_options) { /* Get buffering mode from the options string */ @@ -152,6 +337,24 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) /* Calculate target amount of free space to leave on pages */ buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100; + for (i = 0; i < keyscount; i++) + { + SortSupportFnOids[i] = index_getprocid(index, i + 1, GIST_SORTSUPPORT_PROC); + if (!OidIsValid(SortSupportFnOids[i])) + { + hasallsortsupports = false; + break; + } + } + if (hasallsortsupports) + { + SortSupport sort = palloc0(sizeof(SortSupportData) * keyscount); + for (i = 0; i < keyscount; i++) + OidFunctionCall1(SortSupportFnOids[i], PointerGetDatum(sort + i)); + buildstate.bufferingMode = GIST_BUFFERING_DISABLED; + buildstate.spool = gist_spoolinit(heap, index, sort); + } + /* * We expect to be called exactly once for any index relation. If that's * not the case, big trouble's what we have. @@ -208,6 +411,12 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) gistFreeBuildBuffers(buildstate.gfbb); } + if (buildstate.spool) + { + gist_indexsortbuild(&buildstate); + tuplesort_end(buildstate.spool->sortstate); + } + /* okay, all heap tuples are indexed */ MemoryContextSwitchTo(oldcxt); MemoryContextDelete(buildstate.giststate->tempCxt); @@ -449,14 +658,20 @@ gistBuildCallback(Relation index, GISTBuildState *buildstate = (GISTBuildState *) state; IndexTuple itup; MemoryContext oldCtx; + Datum compressed_values[INDEX_MAX_KEYS]; oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); /* form an index tuple and point it at the heap tuple */ - itup = gistFormTuple(buildstate->giststate, index, values, isnull, true); + itup = gistCompressValuesAndFormTuple(buildstate->giststate, index, values, isnull, true, compressed_values); itup->t_tid = *tid; - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) + if (buildstate->spool) + { + if (tupleIsAlive) + gist_spool(buildstate->spool, (ItemPointer)itup, compressed_values, isnull); + } + else if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) { /* We have buffers, so use them. */ gistBufferingBuildInsert(buildstate, itup); diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index a28ca4a0c4..31552ba698 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -31,6 +31,11 @@ static bool gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy); static bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy); +static int64 part_bits32_by2(uint32 x); +static int64 interleave_bits32(uint32 x, uint32 y); +static inline uint64 point_zorder_internal(Point *p); +static int gist_bbox_fastcmp(Datum x, Datum y, SortSupport ssup); + /* Minimum accepted ratio of split */ #define LIMIT_RATIO 0.3 @@ -1542,6 +1547,8 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } +/* Z-order routines */ +/* Interleave 32 bits with zeroes */ static int64 part_bits32_by2(uint32 x) { @@ -1556,29 +1563,55 @@ part_bits32_by2(uint32 x) return n; } +/* + * Compute Z-order for integers. Also called Morton code. + */ static int64 interleave_bits32(uint32 x, uint32 y) { return part_bits32_by2(x) | (part_bits32_by2(y) << 1); } +/* Compute Z-order for Point */ static inline uint64 point_zorder_internal(Point *p) { + /* + * In this function we need to compute Morton codes for non-integral + * components p->x and p->y. But Morton codes are defined only for + * integral values. + * We expect floats to be in IEEE format, and the sort order of IEEE + * floats is mostly correlated to the binary sort order of the bits + * reinterpreted as an int. It isn't in some special cases, but for this + * use case we don't really care about that, we're just trying to + * encourage locality. + * There is a big jump in integer value (whether signed or + * unsigned) as you cross from positive to negative floats, and then the + * sort order is reversed. This can have negative effect on searches when + * query window touches many quadrants simulatously. In worst case this + * seaches can be x4 more costly. + * We generate a Morton code that interleaves the bits of N integers + * to produce a single integer that preserves locality: things that were + * close in the N dimensional space are close in the resulting integer. + */ union { float f; uint32 i; } a,b; + if (isnan(a.f)) + a.i = PG_INT32_MAX; + if (isnan(b.f)) + b.i = PG_INT32_MAX; a.f = p->x; b.f = p->y; return interleave_bits32(a.i, b.i); } static int -gist_point_fastcmp(Datum x, Datum y, SortSupport ssup) +gist_bbox_fastcmp(Datum x, Datum y, SortSupport ssup) { - Point *p1 = DatumGetPointP(x); - Point *p2 = DatumGetPointP(y); + Point *p1 = &(DatumGetBoxP(x)->low); + Point *p2 = &(DatumGetBoxP(y)->low); uint64 z1 = point_zorder_internal(p1); uint64 z2 = point_zorder_internal(p2); @@ -1590,6 +1623,6 @@ gist_point_sortsupport(PG_FUNCTION_ARGS) { SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); - ssup->comparator = gist_point_fastcmp; + ssup->comparator = gist_bbox_fastcmp; PG_RETURN_VOID(); } diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index dd975b164c..dfc2a7d36c 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -173,6 +173,7 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc, &IsNull); + if (IsNull) continue; @@ -575,6 +576,13 @@ gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf) { Datum compatt[INDEX_MAX_KEYS]; + return gistCompressValuesAndFormTuple(giststate, r, attdata, isnull, isleaf, compatt); +} + +IndexTuple +gistCompressValuesAndFormTuple(GISTSTATE *giststate, Relation r, + Datum attdata[], bool isnull[], bool isleaf, Datum compatt[]) +{ int i; IndexTuple res; @@ -745,14 +753,10 @@ gistpenalty(GISTSTATE *giststate, int attno, * Initialize a new index page */ void -GISTInitBuffer(Buffer b, uint32 f) +gistinitpage(Page page, uint32 f) { - GISTPageOpaque opaque; - Page page; - Size pageSize; - - pageSize = BufferGetPageSize(b); - page = BufferGetPage(b); + GISTPageOpaque opaque; + Size pageSize = BLCKSZ; PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); opaque = GistPageGetOpaque(page); @@ -763,6 +767,18 @@ GISTInitBuffer(Buffer b, uint32 f) opaque->gist_page_id = GIST_PAGE_ID; } +/* + * Initialize a new index buffer + */ +void +GISTInitBuffer(Buffer b, uint32 f) +{ + Page page; + + page = BufferGetPage(b); + gistinitpage(page, f); +} + /* * Verify that a freshly-read page looks sane. */ diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index 0c4fb8c1bf..bbaa7bf6e4 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -140,6 +140,10 @@ gistvalidate(Oid opclassoid) 5, 5, INTERNALOID, opcintype, INT2OID, OIDOID, INTERNALOID); break; + case GIST_SORTSUPPORT_PROC: + ok = check_amproc_signature(procform->amproc, VOIDOID, true, + 1, 1, INTERNALOID); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -259,7 +263,8 @@ gistvalidate(Oid opclassoid) (opclassgroup->functionset & (((uint64) 1) << i)) != 0) continue; /* got it */ if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC || - i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC) + i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC || + i == GIST_SORTSUPPORT_PROC) continue; /* optional methods */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index d02e676aa3..33e2268d4d 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -1095,6 +1095,43 @@ tuplesort_begin_index_hash(Relation heapRel, return state; } +Tuplesortstate * +tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + SortSupport ssup, + int workMem, + SortCoordinate coordinate, + bool randomAccess) +{ + Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, + randomAccess); + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(state->sortcontext); + +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "begin index sort: workMem = %d, randomAccess = %c", + workMem, randomAccess ? 't' : 'f'); +#endif + + state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); + + state->comparetup = comparetup_index_btree; + state->copytup = copytup_index; + state->writetup = writetup_index; + state->readtup = readtup_index; + state->sortKeys = ssup; + + state->heapRel = heapRel; + state->indexRel = indexRel; + + MemoryContextSwitchTo(oldcontext); + + return state; +} + Tuplesortstate * tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 73e43e880a..3a5c78b412 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -35,7 +35,8 @@ #define GIST_EQUAL_PROC 7 #define GIST_DISTANCE_PROC 8 #define GIST_FETCH_PROC 9 -#define GISTNProcs 9 +#define GIST_SORTSUPPORT_PROC 10 +#define GISTNProcs 10 /* * Page opaque data in a GiST index page. diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 18f2b0d98e..89f77ddcae 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -495,12 +495,15 @@ extern IndexTuple gistgetadjusted(Relation r, GISTSTATE *giststate); extern IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf); +extern IndexTuple gistCompressValuesAndFormTuple(GISTSTATE *giststate, Relation r, + Datum attdata[], bool isnull[], bool isleaf, Datum compatt[]); extern OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate); extern void GISTInitBuffer(Buffer b, uint32 f); +extern void gistinitpage(Page page, uint32 f); extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull); diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 75c0152b66..4486327401 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -472,6 +472,8 @@ amproc => 'gist_point_distance' }, { amprocfamily => 'gist/point_ops', amproclefttype => 'point', amprocrighttype => 'point', amprocnum => '9', amproc => 'gist_point_fetch' }, +{ amprocfamily => 'gist/point_ops', amproclefttype => 'point', + amprocrighttype => 'point', amprocnum => '10', amproc => 'gist_point_sortsupport' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', amproc => 'gist_box_consistent' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h index a2fdd3fcd3..989d68462f 100644 --- a/src/include/utils/tuplesort.h +++ b/src/include/utils/tuplesort.h @@ -25,6 +25,7 @@ #include "executor/tuptable.h" #include "storage/dsm.h" #include "utils/relcache.h" +#include "utils/sortsupport.h" /* @@ -208,6 +209,11 @@ extern Tuplesortstate *tuplesort_begin_index_hash(Relation heapRel, uint32 max_buckets, int workMem, SortCoordinate coordinate, bool randomAccess); +extern Tuplesortstate *tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + SortSupport ssup, + int workMem, SortCoordinate coordinate, + bool randomAccess); extern Tuplesortstate *tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, -- 2.21.1 (Apple Git-122.3) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-03-01 10:09  Darafei "Komяpa" Praliaskouski <me@komzpa.net>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Darafei "Komяpa" Praliaskouski @ 2020-03-01 10:09 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

Hello,

Thanks for the patch and working on GiST infrastructure, it's really
valuable for PostGIS use cases and I wait to see this improvement in
PG13.

On Sat, Feb 29, 2020 at 3:13 PM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

> Thomas, I've used your wording almost exactly with explanation how
> point_zorder_internal() works. It has more explanation power than my attempts
> to compose good comment.

PostGIS uses this trick to ensure locality. In PostGIS 3 we enhanced
that trick to have the Hilbert curve instead of Z Order curve.

For visual representation have a look at these links:
 - http://blog.cleverelephant.ca/2019/08/postgis-3-sorting.html - as
it's implemented in PostGIS btree sorting opclass
 - https://observablehq.com/@mourner/hilbert-curve-packing - to
explore general approach.

Indeed if it feels insecure to work with bit magic that implementation
can be left out to extensions.

> There is one design decision that worries me most:
> should we use opclass function or index option to provide this sorting information?
> It is needed only during index creation, actually. And having extra i-class only for fast build
> seems excessive.
> I think we can provide both ways and let opclass developers decide?

Reloption variant looks dirty. It won't cover an index on (id uuid,
geom geometry) where id is duplicated (say, tracked car identifier)
but present in every query - no way to pass such thing as reloption.
I'm also concerned about security of passing a sortsupport function
manually during index creation (what if that's not from the same
extension or even (wrong-)user defined something).

We know for sure it's a good idea for all btree_gist types and
geometry and I don't see a case where user would want to disable it.
Just make it part of operator class, and that would also allow fast
creation of multi-column index.

Thanks.

-- 
Darafei Praliaskouski
Support me: http://patreon.com/komzpa





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-04-03 03:17  Yuri Astrakhan <yuriastrakhan@gmail.com>
  parent: Darafei "Komяpa" Praliaskouski <me@komzpa.net>
  0 siblings, 1 reply; 111+ messages in thread

From: Yuri Astrakhan @ 2020-04-03 03:17 UTC (permalink / raw)
  To: pgsql-hackers@lists.postgresql.org; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>

Awesome addition!  Would it make sense to use x86's BMI2's PDEP instruction, or is the interleave computation too small of a percentage to introduce not-so-easy-to-port code?  Also, I think it needs a bit more documentation to explain the logic, i.e. a link to https://stackoverflow.com/questions/39490345/interleave-bits-efficiently ?  Thx for making it faster :)

^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-04-05 09:48  Darafei "Komяpa" Praliaskouski <me@komzpa.net>
  parent: Yuri Astrakhan <yuriastrakhan@gmail.com>
  0 siblings, 0 replies; 111+ messages in thread

From: Darafei "Komяpa" Praliaskouski @ 2020-04-05 09:48 UTC (permalink / raw)
  To: Yuri Astrakhan <yuriastrakhan@gmail.com>; +Cc: PostgreSQL Developers <pgsql-hackers@lists.postgresql.org>; Andrey Borodin <x4mmm@yandex-team.ru>

Hello Yuri,

PDEP is indeed first thing that comes up when you start googling
z-curve and bit interleaving :)
We had the code with z-curve generating PDEP instruction in PostGIS,
and dropped it since. In sorting, we now utilize sort support / prefix
search, and key generated as Hilbert curve, with fine tuning it for
different projections' geometric properties.

From this patch the most valuable thing for us is the sorting build
infrastructure itself. Maybe to get it a bit more understandable for
people not deep in geometry it makes sense to first expose the
btree_gist datatypes to this thing? So that btree_gist index on
integer will be built exactly the same way the btree index on integer
is built. This will also get everyone a reference point on the
bottlenecks and optimality of patch.

On Fri, Apr 3, 2020 at 10:56 AM Yuri Astrakhan <yuriastrakhan@gmail.com> wrote:
>
> Awesome addition!  Would it make sense to use x86's BMI2's PDEP instruction, or is the interleave computation too small of a percentage to introduce not-so-easy-to-port code?  Also, I think it needs a bit more documentation to explain the logic, i.e. a link to https://stackoverflow.com/questions/39490345/interleave-bits-efficiently ?  Thx for making it faster :)



-- 
Darafei Praliaskouski
Support me: http://patreon.com/komzpa





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-08-18 15:57  Pavel Borisov <pashkin.elfe@gmail.com>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  3 siblings, 0 replies; 111+ messages in thread

From: Pavel Borisov @ 2020-08-18 15:57 UTC (permalink / raw)
  To: pgsql-hackers@lists.postgresql.org; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>

The following review has been posted through the commitfest application:
make installcheck-world:  tested, passed
Implements feature:       tested, passed
Spec compliant:           not tested
Documentation:            not tested

I consider this patch almost ready for commit with minor corrections (see previous message)

The new status of this patch is: Waiting on Author


^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-09 10:28  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  2 siblings, 1 reply; 111+ messages in thread

From: Andrey M. Borodin @ 2020-09-09 10:28 UTC (permalink / raw)
  To: Darafei Komяpa Praliaskouski <me@komzpa.net>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

Thanks Darafei!

> 9 сент. 2020 г., в 12:05, Darafei Komяpa Praliaskouski <me@komzpa.net> написал(а):
> 
> > How does the 'sortsupport' routine interact with 'compress'/'decompress'? Which representation is passed to the comparator routine: the original value from the table, the compressed representation, or the decompressed representation? Do the comparetup_index_btree() and readtup_index() routines agree with that?
> 
> Currently we pass compressed values, which seems not very good.
> But there was a request from PostGIS maintainers to pass values before decompression.
> Darafei, please, correct me if I'm wrong. Also can you please provide link on PostGIS B-tree sorting functions?
> 
> We were expecting to reuse btree opclass for this thing. This way btree_gist extension will become a lot thinner. :)

I think if we aim at reusing B-tree sort support functions we have to pass uncompressed values. They can be a lot bigger and slower in case of PostGIS. We will be sorting actual geometries instead of MBRs.

In my view it's better to implement GiST-specific sort support in btree_gist, rather than trying to reuse existing sort supports.

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-09 12:09  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-09 12:09 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; +Cc: Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 09/09/2020 13:28, Andrey M. Borodin wrote:
> Thanks Darafei!
> 
>> 9 сент. 2020 г., в 12:05, Darafei Komяpa Praliaskouski
>> <me@komzpa.net> написал(а):
>> 
>>> How does the 'sortsupport' routine interact with
>>> 'compress'/'decompress'? Which representation is passed to the
>>> comparator routine: the original value from the table, the
>>> compressed representation, or the decompressed representation? Do
>>> the comparetup_index_btree() and readtup_index() routines agree
>>> with that?
>> 
>> Currently we pass compressed values, which seems not very good. But
>> there was a request from PostGIS maintainers to pass values before
>> decompression. Darafei, please, correct me if I'm wrong. Also can
>> you please provide link on PostGIS B-tree sorting functions?
>> 
>> We were expecting to reuse btree opclass for this thing. This way
>> btree_gist extension will become a lot thinner. :)
> 
> I think if we aim at reusing B-tree sort support functions we have to
> pass uncompressed values. They can be a lot bigger and slower in case
> of PostGIS. We will be sorting actual geometries instead of MBRs.
> 
> In my view it's better to implement GiST-specific sort support in
> btree_gist, rather than trying to reuse existing sort supports.

Yeah, I don't think reusing existing sortsupport functions directly is 
important. The comparison function should be short anyway for 
performance reasons, so it won't be a lot of code to copy-paste. And if 
there are some common subroutines, you can put them in a separate 
internal functions for reuse.

Using the 'compressed' format seems reasonable to me. It's natural to 
the gistbuild.c code, and the comparison routine can 'decompress' itself 
if it wishes. If the decompressions is somewhat expensive, it's 
unfortunate if you need to do it repeatedly in the comparator, but 
tuplesort.c would need pretty big changes to keep around a separate 
in-memory representation compare. However, you could use the sort 
"abbreviation" functionality to mitigate that.

Come to think of it, the point z-order comparator could benefit a lot 
from key abbreviation, too. You could do the point -> zorder conversion 
in the abbreviation routine.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-09 12:20  Darafei "Komяpa" Praliaskouski <me@komzpa.net>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 2 replies; 111+ messages in thread

From: Darafei "Komяpa" Praliaskouski @ 2020-09-09 12:20 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Wed, Sep 9, 2020 at 3:09 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

> On 09/09/2020 13:28, Andrey M. Borodin wrote:
> > Thanks Darafei!
> >
> >> 9 сент. 2020 г., в 12:05, Darafei Komяpa Praliaskouski
> >> <me@komzpa.net> написал(а):
> >>
> >>> How does the 'sortsupport' routine interact with
> >>> 'compress'/'decompress'? Which representation is passed to the
> >>> comparator routine: the original value from the table, the
> >>> compressed representation, or the decompressed representation? Do
> >>> the comparetup_index_btree() and readtup_index() routines agree
> >>> with that?
> >>
> ....
>
> Come to think of it, the point z-order comparator could benefit a lot
> from key abbreviation, too. You could do the point -> zorder conversion
> in the abbreviation routine.
>

That's how it works in PostGIS, only that we moved to more
effecient Hilbert curve:
https://github.com/postgis/postgis/blob/54399b9f6b0f02e8db9444f9f042b8d4ca6d4fa4/postgis/lwgeom_btre...

^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-09 15:39  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Darafei "Komяpa" Praliaskouski <me@komzpa.net>
  1 sibling, 0 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-09 15:39 UTC (permalink / raw)
  To: Darafei "Komяpa" Praliaskouski <me@komzpa.net>; +Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 09/09/2020 15:20, Darafei "Komяpa" Praliaskouski wrote:
> On Wed, Sep 9, 2020 at 3:09 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
> 
>> Come to think of it, the point z-order comparator could benefit a lot
>> from key abbreviation, too. You could do the point -> zorder conversion
>> in the abbreviation routine.
> 
> That's how it works in PostGIS, only that we moved to more
> effecient Hilbert curve:
> https://github.com/postgis/postgis/blob/54399b9f6b0f02e8db9444f9f042b8d4ca6d4fa4/postgis/lwgeom_btre...

Thanks, that's interesting.

I implemented the abbreviated keys for the point opclass, too, and 
noticed that the patch as it was never used it. I reworked the patch so 
that tuplesort_begin_index_gist() is responsible for looking up the 
sortsupport function, like tuplesort_begin_index_btree() does, and uses 
abbreviation when possible.

I think this is pretty much ready for commit now. I'll do a bit more 
testing (do we have regression test coverage for this?), also on a 
SIZEOF_DATUM==4 system since the abbreviation works differently with 
that, and push if nothing new comes up. And clarify the documentation 
and/or comments that the sortsupport function sees "compressed" values.

I wonder if we could use sorting to also speed up building tsvector 
indexes? The values stored there are bit signatures, what would be a 
good sort order for those?

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-09 15:39  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Darafei "Komяpa" Praliaskouski <me@komzpa.net>
  1 sibling, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-09 15:39 UTC (permalink / raw)
  To: Darafei "Komяpa" Praliaskouski <me@komzpa.net>; +Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 09/09/2020 15:20, Darafei "Komяpa" Praliaskouski wrote:
> On Wed, Sep 9, 2020 at 3:09 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
> 
>> Come to think of it, the point z-order comparator could benefit a lot
>> from key abbreviation, too. You could do the point -> zorder conversion
>> in the abbreviation routine.
> 
> That's how it works in PostGIS, only that we moved to more
> effecient Hilbert curve:
> https://github.com/postgis/postgis/blob/54399b9f6b0f02e8db9444f9f042b8d4ca6d4fa4/postgis/lwgeom_btre...

Thanks, that's interesting.

I implemented the abbreviated keys for the point opclass, too, and 
noticed that the patch as it was never used it. I reworked the patch so 
that tuplesort_begin_index_gist() is responsible for looking up the 
sortsupport function, like tuplesort_begin_index_btree() does, and uses 
abbreviation when possible.

I think this is pretty much ready for commit now. I'll do a bit more 
testing (do we have regression test coverage for this?), also on a 
SIZEOF_DATUM==4 system since the abbreviation works differently with 
that, and push if nothing new comes up. And clarify the documentation 
and/or comments that the sortsupport function sees "compressed" values.

I wonder if we could use sorting to also speed up building tsvector 
indexes? The values stored there are bit signatures, what would be a 
good sort order for those?

- Heikki

Attachments:

v17-0001-Add-sort-support-for-point-gist_point_sortsuppor.patch (text/x-patch, 44.9K) download From 3a4d9c14631ae54b983d75433e0286f3dfedf432 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas <heikki.linnakangas@iki.fi> Date: Wed, 9 Sep 2020 18:33:18 +0300 Subject: [PATCH v17 1/1] Add sort support for point gist_point_sortsupport Implement GiST build using sort support We use special sorting function provided by opclass to approximate GiST tree with B-tree-like structure. This approach allows to radically reduce build time in some cases. Author: Andrey Borodin Reviewed-by: Pavel Borisov, Thomas Munro Discussion: https://www.postgresql.org/message-id/1A36620E-CAD8-4267-9067-FB31385E7C0D%40yandex-team.ru --- doc/src/sgml/gist.sgml | 66 ++++ src/backend/access/gist/gistbuild.c | 499 ++++++++++++++++++++---- src/backend/access/gist/gistproc.c | 224 +++++++++++ src/backend/access/gist/gistutil.c | 53 ++- src/backend/access/gist/gistvalidate.c | 6 +- src/backend/access/transam/xloginsert.c | 57 +++ src/backend/utils/sort/sortsupport.c | 34 ++ src/backend/utils/sort/tuplesort.c | 57 +++ src/include/access/gist.h | 3 +- src/include/access/gist_private.h | 3 + src/include/access/xloginsert.h | 2 + src/include/catalog/catversion.h | 1 + src/include/catalog/pg_amproc.dat | 2 + src/include/catalog/pg_proc.dat | 3 + src/include/utils/sortsupport.h | 1 + src/include/utils/tuplesort.h | 4 + 16 files changed, 912 insertions(+), 103 deletions(-) diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml index f9226e7a35c..b049094c811 100644 --- a/doc/src/sgml/gist.sgml +++ b/doc/src/sgml/gist.sgml @@ -259,6 +259,8 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); <function>compress</function> method is omitted. The optional tenth method <function>options</function> is needed if the operator class provides the user-specified parameters. + The <function>sortsupport</function> method is also optional and is used to speed up + building a <acronym>GiST</acronym> index. </para> <variablelist> @@ -1065,6 +1067,70 @@ my_compress(PG_FUNCTION_ARGS) </para> </listitem> </varlistentry> + + <varlistentry> + <term><function>sortsupport</function></term> + <listitem> + <para> + Returns a comparator function to sort data in a way that preserves + locality. It is used by <command>CREATE INDEX</command> and + <command>REINDEX</command>. The quality of the created index depends on + how well the sort order determined by the comparator routine preserves + locality of the inputs. + </para> + <para> + The <function>sortsupport</function> method is optional. If it is not + provided, <command>CREATE INDEX</command> builds the index by inserting + each tuple to the tree using the <function>penalty</function> and + <function>picksplit</function> functions, which is much slower. + </para> + + <para> + The <acronym>SQL</acronym> declaration of the function must look like + this: + +<programlisting> +CREATE OR REPLACE FUNCTION my_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; +</programlisting> + + The argument is a pointer to a <structname>SortSupport</structname> + struct. At a minimum, the function must fill in its comparator field, + the full API is defined in + <filename>src/include/utils/sortsupport.h</filename>. + </para> + + <para> + The matching code in the C module could then follow this skeleton: + +<programlisting> +PG_FUNCTION_INFO_V1(my_sortsupport); + +static int +my_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + /* establish order between x and y by computing some sorting value z */ + + int z1 = ComputeSpatialCode(x); + int z2 = ComputeSpatialCode(y); + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +my_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = my_fastcmp; + PG_RETURN_VOID(); +} +</programlisting> + </para> + </listitem> + </varlistentry> </variablelist> <para> diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index 671b5e9186f..826de23e41e 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -4,6 +4,24 @@ * build algorithm for GiST indexes implementation. * * + * There are two different strategies: + * + * 1. Sort all input tuples, pack the tuple into GiST pages in the sorted + * order. This builds the index from the bottom up, similar to how the + * B-tree build works. + * + * 2. Start with an empty index, and insert all tuples one by one. + * + * The sorted method is used if the operator classes for all columns have + * a 'sortsupport' defined. Otherwise, we resort to the second strategy. + * + * The second strategy can optionally use buffers at different levels of + * the tree to reduce I/O, see "Buffering build algorithm" in the README + * for a more detailed explanation. It initially calls insert over and over, + * but switches to the buffered algorithm after a certain number of tuples + * (unless buffering mode is disabled). + * + * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * @@ -28,6 +46,7 @@ #include "storage/smgr.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/tuplesort.h" /* Step of index tuples for check whether to switch to buffering build mode */ #define BUFFERING_MODE_SWITCH_CHECK_STEP 256 @@ -40,8 +59,14 @@ */ #define BUFFERING_MODE_TUPLE_SIZE_STATS_TARGET 4096 +/* + * Strategy used to build the index. It can change between the + * GIST_BUFFERING_* modes on the fly, but if the Sorted method is used, + * that needs to be decided up-front and cannot be changed afterwards. + */ typedef enum { + GIST_SORTED_BUILD, /* bottom-up build by sorting */ GIST_BUFFERING_DISABLED, /* in regular build mode and aren't going to * switch */ GIST_BUFFERING_AUTO, /* in regular build mode, but will switch to @@ -51,7 +76,7 @@ typedef enum * before switching to the buffering build * mode */ GIST_BUFFERING_ACTIVE /* in buffering build mode */ -} GistBufferingMode; +} GistBuildMode; /* Working state for gistbuild and its callback */ typedef struct @@ -60,23 +85,57 @@ typedef struct Relation heaprel; GISTSTATE *giststate; - int64 indtuples; /* number of tuples indexed */ - int64 indtuplesSize; /* total size of all indexed tuples */ - Size freespace; /* amount of free space to leave on pages */ + GistBuildMode buildMode; + + int64 indtuples; /* number of tuples indexed */ + /* * Extra data structures used during a buffering build. 'gfbb' contains * information related to managing the build buffers. 'parentMap' is a * lookup table of the parent of each internal page. */ + int64 indtuplesSize; /* total size of all indexed tuples */ GISTBuildBuffers *gfbb; HTAB *parentMap; - GistBufferingMode bufferingMode; + /* + * Extra data structures used during a sorting build. + */ + Tuplesortstate *sortstate; /* state data for tuplesort.c */ + + BlockNumber pages_allocated; + BlockNumber pages_written; + + int ready_num_pages; + BlockNumber ready_blknos[XLR_MAX_BLOCK_ID]; + Page ready_pages[XLR_MAX_BLOCK_ID]; } GISTBuildState; +/* + * In sorted build, we use a stack of these structs, one for each level, + * to pack the tuples in pages. + */ +typedef struct GistSortedBuildPageState +{ + Page page; + struct GistSortedBuildPageState *parent; /* Upper level, if any */ +} GistSortedBuildPageState; + /* prototypes for private functions */ + +static void gistSortedBuildCallback(Relation index, ItemPointer tid, + Datum *values, bool *isnull, + bool tupleIsAlive, void *state); +static void gist_indexsortbuild(GISTBuildState *state); +static void gist_indexsortbuild_pagestate_add(GISTBuildState *state, + GistSortedBuildPageState *pagestate, + IndexTuple itup); +static void gist_indexsortbuild_pagestate_flush(GISTBuildState *state, + GistSortedBuildPageState *pagestate); +static void gist_indexsortbuild_flush_ready_pages(GISTBuildState *state); + static void gistInitBuffering(GISTBuildState *buildstate); static int calculatePagesPerBuffer(GISTBuildState *buildstate, int levelStep); static void gistBuildCallback(Relation index, @@ -107,10 +166,9 @@ static void gistMemorizeParent(GISTBuildState *buildstate, BlockNumber child, static void gistMemorizeAllDownlinks(GISTBuildState *buildstate, Buffer parent); static BlockNumber gistGetParent(GISTBuildState *buildstate, BlockNumber child); + /* - * Main entry point to GiST index build. Initially calls insert over and over, - * but switches to more efficient buffering build algorithm after a certain - * number of tuples (unless buffering mode is disabled). + * Main entry point to GiST index build. */ IndexBuildResult * gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) @@ -118,124 +176,397 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) IndexBuildResult *result; double reltuples; GISTBuildState buildstate; - Buffer buffer; - Page page; MemoryContext oldcxt = CurrentMemoryContext; int fillfactor; + Oid SortSupportFnOids[INDEX_MAX_KEYS]; + bool hasallsortsupports; + int keyscount = IndexRelationGetNumberOfKeyAttributes(index); + GiSTOptions *options = NULL; + + /* + * We expect to be called exactly once for any index relation. If that's + * not the case, big trouble's what we have. + */ + if (RelationGetNumberOfBlocks(index) != 0) + elog(ERROR, "index \"%s\" already contains data", + RelationGetRelationName(index)); + + if (index->rd_options) + options = (GiSTOptions *) index->rd_options; buildstate.indexrel = index; buildstate.heaprel = heap; + buildstate.sortstate = NULL; + buildstate.giststate = initGISTstate(index); - if (index->rd_options) + /* + * Create a temporary memory context that is reset once for each tuple + * processed. (Note: we don't bother to make this a child of the + * giststate's scanCxt, so we have to delete it separately at the end.) + */ + buildstate.giststate->tempCxt = createTempGistContext(); + + /* + * Choose build strategy. If all keys support sorting, do that. Otherwise + * the default strategy is switch to buffering mode when the index grows + * too large to fit in cache. + */ + hasallsortsupports = true; + for (int i = 0; i < keyscount; i++) { - /* Get buffering mode from the options string */ - GiSTOptions *options = (GiSTOptions *) index->rd_options; + SortSupportFnOids[i] = index_getprocid(index, i + 1, GIST_SORTSUPPORT_PROC); + if (!OidIsValid(SortSupportFnOids[i])) + { + hasallsortsupports = false; + break; + } + } + if (hasallsortsupports) + { + buildstate.buildMode = GIST_SORTED_BUILD; + } + else if (options) + { if (options->buffering_mode == GIST_OPTION_BUFFERING_ON) - buildstate.bufferingMode = GIST_BUFFERING_STATS; + buildstate.buildMode = GIST_BUFFERING_STATS; else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF) - buildstate.bufferingMode = GIST_BUFFERING_DISABLED; + buildstate.buildMode = GIST_BUFFERING_DISABLED; else - buildstate.bufferingMode = GIST_BUFFERING_AUTO; - - fillfactor = options->fillfactor; + buildstate.buildMode = GIST_BUFFERING_AUTO; } else { - /* - * By default, switch to buffering mode when the index grows too large - * to fit in cache. - */ - buildstate.bufferingMode = GIST_BUFFERING_AUTO; - fillfactor = GIST_DEFAULT_FILLFACTOR; + buildstate.buildMode = GIST_BUFFERING_AUTO; } - /* Calculate target amount of free space to leave on pages */ + + /* + * Calculate target amount of free space to leave on pages. + */ + fillfactor = options ? options->fillfactor : GIST_DEFAULT_FILLFACTOR; buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100; /* - * We expect to be called exactly once for any index relation. If that's - * not the case, big trouble's what we have. + * Build the index using the chosen strategy. */ - if (RelationGetNumberOfBlocks(index) != 0) - elog(ERROR, "index \"%s\" already contains data", - RelationGetRelationName(index)); + buildstate.indtuples = 0; + buildstate.indtuplesSize = 0; - /* no locking is needed */ - buildstate.giststate = initGISTstate(index); + if (buildstate.buildMode == GIST_SORTED_BUILD) + { + /* + * Sort all data, build the index from bottom up. + */ + buildstate.sortstate = tuplesort_begin_index_gist(heap, + index, + maintenance_work_mem, + NULL, + false); + + /* Scan the table, adding all tuples to the tuplesort */ + reltuples = table_index_build_scan(heap, index, indexInfo, true, true, + gistSortedBuildCallback, + (void *) &buildstate, NULL); + + /* + * Perform the sort and build index pages. + */ + tuplesort_performsort(buildstate.sortstate); + + gist_indexsortbuild(&buildstate); + + tuplesort_end(buildstate.sortstate); + } + else + { + /* + * Initialize an empty index and insert all tuples, possibly using + * buffers on intermediate levels. + */ + Buffer buffer; + Page page; + + /* initialize the root page */ + buffer = gistNewBuffer(index); + Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO); + page = BufferGetPage(buffer); + + START_CRIT_SECTION(); + + GISTInitBuffer(buffer, F_LEAF); + + MarkBufferDirty(buffer); + PageSetLSN(page, GistBuildLSN); + + UnlockReleaseBuffer(buffer); + + END_CRIT_SECTION(); + + /* Scan the table, inserting all the tuples to the index. */ + reltuples = table_index_build_scan(heap, index, indexInfo, true, true, + gistBuildCallback, + (void *) &buildstate, NULL); + + /* + * If buffering was used, flush out all the tuples that are still in + * the buffers. + */ + if (buildstate.buildMode == GIST_BUFFERING_ACTIVE) + { + elog(DEBUG1, "all tuples processed, emptying buffers"); + gistEmptyAllBuffers(&buildstate); + gistFreeBuildBuffers(buildstate.gfbb); + } + + /* + * We didn't write WAL records as we built the index, so if + * WAL-logging is required, write all pages to the WAL now. + */ + if (RelationNeedsWAL(index)) + { + log_newpage_range(index, MAIN_FORKNUM, + 0, RelationGetNumberOfBlocks(index), + true); + } + } + + /* okay, all heap tuples are indexed */ + MemoryContextSwitchTo(oldcxt); + MemoryContextDelete(buildstate.giststate->tempCxt); + + freeGISTstate(buildstate.giststate); /* - * Create a temporary memory context that is reset once for each tuple - * processed. (Note: we don't bother to make this a child of the - * giststate's scanCxt, so we have to delete it separately at the end.) + * Return statistics */ - buildstate.giststate->tempCxt = createTempGistContext(); + result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); - /* initialize the root page */ - buffer = gistNewBuffer(index); - Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO); - page = BufferGetPage(buffer); + result->heap_tuples = reltuples; + result->index_tuples = (double) buildstate.indtuples; + + return result; +} + +/*------------------------------------------------------------------------- + * Routines for sorted build + *------------------------------------------------------------------------- + */ + +/* + * Per-tuple callback for table_index_build_scan. + */ +static void +gistSortedBuildCallback(Relation index, + ItemPointer tid, + Datum *values, + bool *isnull, + bool tupleIsAlive, + void *state) +{ + GISTBuildState *buildstate = (GISTBuildState *) state; + MemoryContext oldCtx; + Datum compressed_values[INDEX_MAX_KEYS]; - START_CRIT_SECTION(); + oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); - GISTInitBuffer(buffer, F_LEAF); + /* Form an index tuple and point it at the heap tuple */ + gistCompressValues(buildstate->giststate, index, + values, isnull, + true, compressed_values); - MarkBufferDirty(buffer); - PageSetLSN(page, GistBuildLSN); + tuplesort_putindextuplevalues(buildstate->sortstate, + buildstate->indexrel, + tid, + compressed_values, isnull); - UnlockReleaseBuffer(buffer); + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(buildstate->giststate->tempCxt); - END_CRIT_SECTION(); + /* Update tuple count. */ + buildstate->indtuples += 1; +} - /* build the index */ - buildstate.indtuples = 0; - buildstate.indtuplesSize = 0; +/* + * Build GiST index from bottom up from pre-sorted tuples. + */ +static void +gist_indexsortbuild(GISTBuildState *state) +{ + IndexTuple itup; + GistSortedBuildPageState *leafstate; + GistSortedBuildPageState *pagestate; + Page page; + + state->pages_allocated = 0; + state->pages_written = 0; + state->ready_num_pages = 0; /* - * Do the heap scan. + * Write an empty page as a placeholder for the root page. It will be + * replaced with the real root page at the end. */ - reltuples = table_index_build_scan(heap, index, indexInfo, true, true, - gistBuildCallback, - (void *) &buildstate, NULL); + page = palloc0(BLCKSZ); + smgrextend(state->indexrel->rd_smgr, MAIN_FORKNUM, GIST_ROOT_BLKNO, + page, true); + state->pages_allocated++; + state->pages_written++; + + /* Allocate a scratch page in memory to collect the tuples */ + leafstate = palloc(sizeof(GistSortedBuildPageState)); + leafstate->page = page; + leafstate->parent = NULL; + gistinitpage(page, F_LEAF); /* - * If buffering was used, flush out all the tuples that are still in the - * buffers. + * Fill index pages with tuples in the sorted order. */ - if (buildstate.bufferingMode == GIST_BUFFERING_ACTIVE) + while ((itup = tuplesort_getindextuple(state->sortstate, true)) != NULL) { - elog(DEBUG1, "all tuples processed, emptying buffers"); - gistEmptyAllBuffers(&buildstate); - gistFreeBuildBuffers(buildstate.gfbb); + gist_indexsortbuild_pagestate_add(state, leafstate, itup); } - /* okay, all heap tuples are indexed */ - MemoryContextSwitchTo(oldcxt); - MemoryContextDelete(buildstate.giststate->tempCxt); - - freeGISTstate(buildstate.giststate); - /* - * We didn't write WAL records as we built the index, so if WAL-logging is - * required, write all pages to the WAL now. + * Write out the partially full non-root pages. + * + * Keep in mind that flush can build a new root. */ - if (RelationNeedsWAL(index)) + pagestate = leafstate; + while (pagestate->parent != NULL) { - log_newpage_range(index, MAIN_FORKNUM, - 0, RelationGetNumberOfBlocks(index), - true); + GistSortedBuildPageState *parent; + + gist_indexsortbuild_pagestate_flush(state, pagestate); + parent = pagestate->parent; + pfree(pagestate->page); + pfree(pagestate); + pagestate = parent; } + gist_indexsortbuild_flush_ready_pages(state); + + /* Write out the root */ + smgrwrite(state->indexrel->rd_smgr, MAIN_FORKNUM, GIST_ROOT_BLKNO, + pagestate->page, true); + if (RelationNeedsWAL(state->indexrel)) + log_newpage(&state->indexrel->rd_node, MAIN_FORKNUM, GIST_ROOT_BLKNO, + pagestate->page, true); + + pfree(pagestate->page); + pfree(pagestate); +} + +/* + * Add tuple to a page. If the pages is full, write it out and re-initialize + * a new page first. + */ +static void +gist_indexsortbuild_pagestate_add(GISTBuildState *state, + GistSortedBuildPageState * pagestate, + IndexTuple itup) +{ + Page page = pagestate->page; + + /* Does the tuple fit? If not, flush */ + if (PageGetFreeSpace(page) < IndexTupleSize(itup) + sizeof(ItemIdData) + state->freespace) + gist_indexsortbuild_pagestate_flush(state, pagestate); + + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); +} + +static void +gist_indexsortbuild_pagestate_flush(GISTBuildState *state, + GistSortedBuildPageState * pagestate) +{ + GistSortedBuildPageState *parent; + IndexTuple *itvec; + IndexTuple union_tuple; + int vect_len; + bool isleaf; + BlockNumber blkno; + + if (state->ready_num_pages == XLR_MAX_BLOCK_ID) + gist_indexsortbuild_flush_ready_pages(state); + /* - * Return statistics + * The page is now complete. Assign a block number to it, and add it to + * the list of finished pages. (We don't write it out immediately, because + * we want to WAL-log the pages in batches.) */ - result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); + blkno = state->pages_allocated++; + state->ready_blknos[state->ready_num_pages] = blkno; + state->ready_pages[state->ready_num_pages] = pagestate->page; + state->ready_num_pages++; - result->heap_tuples = reltuples; - result->index_tuples = (double) buildstate.indtuples; + /* check once per page */ + CHECK_FOR_INTERRUPTS(); - return result; + isleaf = GistPageIsLeaf(pagestate->page); + + /* + * Form a downlink tuple to represent all the tuples on the page. + */ + itvec = gistextractpage(pagestate->page, &vect_len); + union_tuple = gistunion(state->indexrel, itvec, vect_len, + state->giststate); + ItemPointerSetBlockNumber(&(union_tuple->t_tid), blkno); + pfree(itvec); + + /* + * Insert the downlink to the parent page. If this was the root, create a + * new page as the parent, which becomes the new root. + */ + parent = pagestate->parent; + if (parent == NULL) + { + parent = palloc(sizeof(GistSortedBuildPageState)); + parent->page = (Page) palloc(BLCKSZ); + parent->parent = NULL; + gistinitpage(parent->page, 0); + + pagestate->parent = parent; + } + gist_indexsortbuild_pagestate_add(state, parent, union_tuple); + pfree(union_tuple); + + /* Re-initialize the page buffer for next page on this level. */ + pagestate->page = palloc(BLCKSZ); + gistinitpage(pagestate->page, isleaf ? F_LEAF : 0); } +static void +gist_indexsortbuild_flush_ready_pages(GISTBuildState *state) +{ + if (state->ready_num_pages == 0) + return; + + for (int i = 0; i < state->ready_num_pages; i++) + { + /* + * currently, the blocks must be buffered in order. Otherwise we + * should do a similar smgrextend/ smgrwrite dance as in nbtsort.c + */ + if (state->ready_blknos[i] != state->pages_written) + elog(ERROR, "unexpected block number to flush GiST sorting build"); + + smgrextend(state->indexrel->rd_smgr, + MAIN_FORKNUM, + state->pages_written++, + state->ready_pages[i], + true); + } + + if (RelationNeedsWAL(state->indexrel)) + log_newpages(&state->indexrel->rd_node, MAIN_FORKNUM, state->ready_num_pages, + state->ready_blknos, state->ready_pages, true); + state->ready_num_pages = 0; +} + + +/*------------------------------------------------------------------------- + * Routines for non-sorted build + *------------------------------------------------------------------------- + */ + /* * Attempt to switch to buffering mode. * @@ -375,7 +706,7 @@ gistInitBuffering(GISTBuildState *buildstate) if (levelStep <= 0) { elog(DEBUG1, "failed to switch to buffered GiST build"); - buildstate->bufferingMode = GIST_BUFFERING_DISABLED; + buildstate->buildMode = GIST_BUFFERING_DISABLED; return; } @@ -392,7 +723,7 @@ gistInitBuffering(GISTBuildState *buildstate) gistInitParentMap(buildstate); - buildstate->bufferingMode = GIST_BUFFERING_ACTIVE; + buildstate->buildMode = GIST_BUFFERING_ACTIVE; elog(DEBUG1, "switched to buffered GiST build; level step = %d, pagesPerBuffer = %d", levelStep, pagesPerBuffer); @@ -453,10 +784,12 @@ gistBuildCallback(Relation index, oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); /* form an index tuple and point it at the heap tuple */ - itup = gistFormTuple(buildstate->giststate, index, values, isnull, true); + itup = gistFormTuple(buildstate->giststate, index, + values, isnull, + true); itup->t_tid = *tid; - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) + if (buildstate->buildMode == GIST_BUFFERING_ACTIVE) { /* We have buffers, so use them. */ gistBufferingBuildInsert(buildstate, itup); @@ -478,7 +811,7 @@ gistBuildCallback(Relation index, MemoryContextSwitchTo(oldCtx); MemoryContextReset(buildstate->giststate->tempCxt); - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE && + if (buildstate->buildMode == GIST_BUFFERING_ACTIVE && buildstate->indtuples % BUFFERING_MODE_TUPLE_SIZE_STATS_TARGET == 0) { /* Adjust the target buffer size now */ @@ -493,10 +826,10 @@ gistBuildCallback(Relation index, * To avoid excessive calls to smgrnblocks(), only check this every * BUFFERING_MODE_SWITCH_CHECK_STEP index tuples */ - if ((buildstate->bufferingMode == GIST_BUFFERING_AUTO && + if ((buildstate->buildMode == GIST_BUFFERING_AUTO && buildstate->indtuples % BUFFERING_MODE_SWITCH_CHECK_STEP == 0 && effective_cache_size < smgrnblocks(index->rd_smgr, MAIN_FORKNUM)) || - (buildstate->bufferingMode == GIST_BUFFERING_STATS && + (buildstate->buildMode == GIST_BUFFERING_STATS && buildstate->indtuples >= BUFFERING_MODE_TUPLE_SIZE_STATS_TARGET)) { /* diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 9ace64c3c4a..ce078426526 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -24,12 +24,17 @@ #include "utils/builtins.h" #include "utils/float.h" #include "utils/geo_decls.h" +#include "utils/sortsupport.h" static bool gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy); static bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy); +static uint64 part_bits32_by2(uint32 x); +static uint32 ieee_float32_to_uint32(float f); +static uint64 point_zorder_internal(Point *p); + /* Minimum accepted ratio of split */ #define LIMIT_RATIO 0.3 @@ -1540,3 +1545,222 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } + +/* Z-order routines */ + +/* + * Compute Z-order for a point + * + * Map a two-dimensional point to a single integer, in a way that preserves + * locality. Points that are close in the two-dimensional space are mapped to + * integer that are not far from each other. We do that by interleaving the + * bits in the X and Y components, this is called a Z-order or Morton Code. + * + * A Morton Code is normally defined only for integers, but the X and Y values + * of a point are floating point. We expect floats to be in IEEE format, and + * the sort order of IEEE floats is mostly correlated to the binary sort order + * of the bits reinterpreted as an int. It isn't in some special cases, but + * for this use case we don't really care about that, we're just trying to + * encourage locality. + */ +static uint64 +point_zorder_internal(Point *p) +{ + uint32 x = ieee_float32_to_uint32(p->x); + uint32 y = ieee_float32_to_uint32(p->y); + + /* Interleave the bits */ + return part_bits32_by2(x) | (part_bits32_by2(y) << 1); +} + +/* Interleave 32 bits with zeroes */ +static uint64 +part_bits32_by2(uint32 x) +{ + uint64 n = x; + + n = (n | (n << 16)) & UINT64CONST(0x0000FFFF0000FFFF); + n = (n | (n << 8)) & UINT64CONST(0x00FF00FF00FF00FF); + n = (n | (n << 4)) & UINT64CONST(0x0F0F0F0F0F0F0F0F); + n = (n | (n << 2)) & UINT64CONST(0x3333333333333333); + n = (n | (n << 1)) & UINT64CONST(0x5555555555555555); + + return n; +} + +/* + * Convert a 32-bit IEEE float to uint32 in a way that preserves the ordering. + */ +static uint32 +ieee_float32_to_uint32(float f) +{ + /*---- + * + * IEEE 754 floating point format + * ------------------------------ + * + * IEEE 754 floating point numbers have this format: + * + * exponent (8 bits) + * | + * s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm + * | | + * sign mantissa (23 bits) + * + * Infinity has all bits in the exponent set and the mantissa is + * all-zeros. Negative infinity is the same but with the sign bit set. + * + * NaNs are represented with all bits in the exponent set, and the least + * significant bit in the mantissa also set. The rest of the mantissa bits + * can be used to distinguish different kinds of NaNs. + * + * The IEEE format has the nice property that when you take the bit + * representation and interpret it as an integer, the order is preserved, + * except for the sign. That holds for the +-Infinity values too. + * + * Mapping to uint32 + * ----------------- + * + * In order to have a smooth transition from negative to positive numbers, + * we map floats to unsigned integers like this: + * + * x < 0 to range 0-7FFFFFFF + * x = 0 to value 8000000 (both positive and negative zero) + * x > 0 to range 8000001-FFFFFFFF + * + * We don't care to distinguish different kind of NaNs, so they are all + * mapped to the same arbitrary value, FFFFFFFF. Because of the IEEE bit + * representation of NaNs, there aren't any non-NaN values that would be + * mapped to FFFFFFFF. In fact, there is a range of unused values on both + * ends of the uint32 space. + */ + if (isnan(f)) + return 0xFFFFFFFF; + else + { + union + { + float f; + uint32 i; + } u; + + u.f = f; + + /* Check the sign bit */ + if ((u.i & 0x80000000) != 0) + { + /* + * Map the negative value to range 0-7FFFFFFF. This flips the sign + * bit to 0 in the same instruction. + */ + Assert(f < 0); + u.i ^= 0xFFFFFFFF; + } + else + { + /* Map the positive value (or 0) to range 80000000-FFFFFFFF */ + u.i |= 0x80000000; + } + + return u.i; + } +} + +/* + * Compare the Z-order of points. + */ +static int +gist_bbox_zorder_cmp(Datum a, Datum b, SortSupport ssup) +{ + Point *p1 = &(DatumGetBoxP(a)->low); + Point *p2 = &(DatumGetBoxP(b)->low); + uint64 z1 = point_zorder_internal(p1); + uint64 z2 = point_zorder_internal(p2); + + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * Abbreviated version of Z-order comparison. + * + * The abbreviated format is a Z-order value computed from the two 32-bit + * floats. If SIZEOF_DATUM == 8, the 64-bit Z-order value fits fully in the + * abbreviated format, so the "full comparator" can just return 0. This + * assumes that the sort code always calls the abbreviated comparator first, + * so that if the full comparator is called, we already know that the + * abbreviated comparator deemed the values equal. If SIZEOF_DATUM == 4, we + * store the upper half of the Z-order value, and the tie-breaker computes all + * the bits. + */ +static Datum +gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup) +{ + Point *p = &(DatumGetBoxP(original)->low); + uint64 z; + + z = point_zorder_internal(p); + + PG_RETURN_UINT64(z); +} + +static int +gist_bbox_zorder_cmp_abbrev(Datum a, Datum b, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +static int +gist_bbox_zorder_full_cmp(Datum a, Datum b, SortSupport ssup) +{ + /* + * On 64-bit systems, the abbreviation is not lossy, so if the + * abbreviated comparator returned 0, the values really are equal. + */ +#if SIZEOF_DATUM == 8 + return 0; +#else + return gist_bbox_zorder_cmp(a, b, ssup); +#endif +} + +/* + * We never consider aborting the abbreviation. (Should we, + * when SIZEOF_DATUM == 4?) + */ +static bool +gist_bbox_zorder_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gist_point_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gist_bbox_zorder_cmp_abbrev; + ssup->abbrev_converter = gist_bbox_zorder_abbrev_convert; + ssup->abbrev_abort = gist_bbox_zorder_abbrev_abort; + ssup->abbrev_full_comparator = gist_bbox_zorder_full_cmp; + } + else + { + ssup->comparator = gist_bbox_zorder_cmp; + } + PG_RETURN_VOID(); +} diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 0516059e3dd..615b5ade233 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -572,12 +572,31 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, - Datum attdata[], bool isnull[], bool isleaf) + Datum *attdata, bool *isnull, bool isleaf) { Datum compatt[INDEX_MAX_KEYS]; - int i; IndexTuple res; + gistCompressValues(giststate, r, attdata, isnull, isleaf, compatt); + + res = index_form_tuple(isleaf ? giststate->leafTupdesc : + giststate->nonLeafTupdesc, + compatt, isnull); + + /* + * The offset number on tuples on internal pages is unused. For historical + * reasons, it is set to 0xffff. + */ + ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff); + return res; +} + +void +gistCompressValues(GISTSTATE *giststate, Relation r, + Datum *attdata, bool *isnull, bool isleaf, Datum *compatt) +{ + int i; + /* * Call the compress method on each attribute. */ @@ -617,17 +636,6 @@ gistFormTuple(GISTSTATE *giststate, Relation r, compatt[i] = attdata[i]; } } - - res = index_form_tuple(isleaf ? giststate->leafTupdesc : - giststate->nonLeafTupdesc, - compatt, isnull); - - /* - * The offset number on tuples on internal pages is unused. For historical - * reasons, it is set to 0xffff. - */ - ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff); - return res; } /* @@ -745,14 +753,11 @@ gistpenalty(GISTSTATE *giststate, int attno, * Initialize a new index page */ void -GISTInitBuffer(Buffer b, uint32 f) +gistinitpage(Page page, uint32 f) { GISTPageOpaque opaque; - Page page; - Size pageSize; + Size pageSize = BLCKSZ; - pageSize = BufferGetPageSize(b); - page = BufferGetPage(b); PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); opaque = GistPageGetOpaque(page); @@ -763,6 +768,18 @@ GISTInitBuffer(Buffer b, uint32 f) opaque->gist_page_id = GIST_PAGE_ID; } +/* + * Initialize a new index buffer + */ +void +GISTInitBuffer(Buffer b, uint32 f) +{ + Page page; + + page = BufferGetPage(b); + gistinitpage(page, f); +} + /* * Verify that a freshly-read page looks sane. */ diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index 2b9ab693be1..8a14620fab2 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -143,6 +143,10 @@ gistvalidate(Oid opclassoid) case GIST_OPTIONS_PROC: ok = check_amoptsproc_signature(procform->amproc); break; + case GIST_SORTSUPPORT_PROC: + ok = check_amproc_signature(procform->amproc, VOIDOID, true, + 1, 1, INTERNALOID); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -263,7 +267,7 @@ gistvalidate(Oid opclassoid) continue; /* got it */ if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC || i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC || - i == GIST_OPTIONS_PROC) + i == GIST_OPTIONS_PROC || i == GIST_SORTSUPPORT_PROC) continue; /* optional methods */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c index c526bb19281..1f0e4e01e69 100644 --- a/src/backend/access/transam/xloginsert.c +++ b/src/backend/access/transam/xloginsert.c @@ -1019,6 +1019,63 @@ log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno, return recptr; } +/* + * Like log_newpage(), but allows logging multiple pages in one operation. + * It is more efficient than calling log_newpage() for each page separately, + * because we can write multiple pages in a single WAL record. + */ +void +log_newpages(RelFileNode *rnode, ForkNumber forkNum, int num_pages, + BlockNumber *blknos, Page *pages, bool page_std) +{ + int flags; + XLogRecPtr recptr; + int i; + int j; + + flags = REGBUF_FORCE_IMAGE; + if (page_std) + flags |= REGBUF_STANDARD; + + /* + * Iterate over all the pages. They are collected into batches of + * XLR_MAX_BLOCK_ID pages, and a single WAL-record is written for each + * batch. + */ + XLogEnsureRecordSpace(XLR_MAX_BLOCK_ID - 1, 0); + + i = 0; + while (i < num_pages) + { + int batch_start = i; + int nbatch; + + XLogBeginInsert(); + + nbatch = 0; + while (nbatch < XLR_MAX_BLOCK_ID && i < num_pages) + { + XLogRegisterBlock(nbatch, rnode, forkNum, blknos[i], pages[i], flags); + i++; + nbatch++; + } + + recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI); + + for (j = batch_start; j < i; j++) + { + /* + * The page may be uninitialized. If so, we can't set the LSN because that + * would corrupt the page. + */ + if (!PageIsNew(pages[j])) + { + PageSetLSN(pages[j], recptr); + } + } + } +} + /* * Write a WAL record containing a full image of a page. * diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c index fcfe6e831a1..94a9de317e9 100644 --- a/src/backend/utils/sort/sortsupport.c +++ b/src/backend/utils/sort/sortsupport.c @@ -15,6 +15,7 @@ #include "postgres.h" +#include "access/gist.h" #include "access/nbtree.h" #include "catalog/pg_am.h" #include "fmgr.h" @@ -175,3 +176,36 @@ PrepareSortSupportFromIndexRel(Relation indexRel, int16 strategy, FinishSortSupportFunction(opfamily, opcintype, ssup); } + +/* + * Fill in SortSupport given an GiST index relation + * + * Caller must previously have zeroed the SortSupportData structure and then + * filled in ssup_cxt, ssup_attno, ssup_collation, and ssup_nulls_first. This + * will fill in ssup_reverse (always false for GiST index build), as well as + * the comparator function pointer. + */ +void +PrepareSortSupportFromGistIndexRel(Relation indexRel, SortSupport ssup) +{ + Oid opfamily = indexRel->rd_opfamily[ssup->ssup_attno - 1]; + Oid opcintype = indexRel->rd_opcintype[ssup->ssup_attno - 1]; + Oid sortSupportFunction; + + Assert(ssup->comparator == NULL); + + if (indexRel->rd_rel->relam != GIST_AM_OID) + elog(ERROR, "unexpected non-gist AM: %u", indexRel->rd_rel->relam); + ssup->ssup_reverse = false; + + /* + * Look up the sort support function. This is simpler than for B-tree + * indexes because we don't support the old-style btree comparators. + */ + sortSupportFunction = get_opfamily_proc(opfamily, opcintype, opcintype, + GIST_SORTSUPPORT_PROC); + if (!OidIsValid(sortSupportFunction)) + elog(ERROR, "missing support function %d(%u,%u) in opfamily %u", + GIST_SORTSUPPORT_PROC, opcintype, opcintype, opfamily); + OidFunctionCall1(sortSupportFunction, PointerGetDatum(ssup)); +} diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 3c49476483b..fb4ac4d2f9f 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -1167,6 +1167,63 @@ tuplesort_begin_index_hash(Relation heapRel, return state; } +Tuplesortstate * +tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + int workMem, + SortCoordinate coordinate, + bool randomAccess) +{ + Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, + randomAccess); + MemoryContext oldcontext; + int i; + + oldcontext = MemoryContextSwitchTo(state->sortcontext); + +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "begin index sort: workMem = %d, randomAccess = %c", + workMem, randomAccess ? 't' : 'f'); +#endif + + state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); + + state->comparetup = comparetup_index_btree; + state->copytup = copytup_index; + state->writetup = writetup_index; + state->readtup = readtup_index; + + state->heapRel = heapRel; + state->indexRel = indexRel; + + /* Prepare SortSupport data for each column */ + state->sortKeys = (SortSupport) palloc0(state->nKeys * + sizeof(SortSupportData)); + + for (i = 0; i < state->nKeys; i++) + { + SortSupport sortKey = state->sortKeys + i; + + sortKey->ssup_cxt = CurrentMemoryContext; + sortKey->ssup_collation = indexRel->rd_indcollation[i]; + sortKey->ssup_nulls_first = false; + sortKey->ssup_attno = i + 1; + /* Convey if abbreviation optimization is applicable in principle */ + sortKey->abbreviate = (i == 0); + + AssertState(sortKey->ssup_attno != 0); + + /* Look for a sort support function */ + PrepareSortSupportFromGistIndexRel(indexRel, sortKey); + } + + MemoryContextSwitchTo(oldcontext); + + return state; +} + Tuplesortstate * tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 4994351697c..4f6dae9a76b 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -37,7 +37,8 @@ #define GIST_DISTANCE_PROC 8 #define GIST_FETCH_PROC 9 #define GIST_OPTIONS_PROC 10 -#define GISTNProcs 10 +#define GIST_SORTSUPPORT_PROC 11 +#define GISTNProcs 11 /* * Page opaque data in a GiST index page. diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 02e985549f6..b68c01a5f24 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -501,12 +501,15 @@ extern IndexTuple gistgetadjusted(Relation r, GISTSTATE *giststate); extern IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf); +extern void gistCompressValues(GISTSTATE *giststate, Relation r, + Datum *attdata, bool *isnull, bool isleaf, Datum *compatt); extern OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate); extern void GISTInitBuffer(Buffer b, uint32 f); +extern void gistinitpage(Page page, uint32 f); extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull); diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h index 63df25ae90f..674c376053e 100644 --- a/src/include/access/xloginsert.h +++ b/src/include/access/xloginsert.h @@ -54,6 +54,8 @@ extern bool XLogCheckBufferNeedsBackup(Buffer buffer); extern XLogRecPtr log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blk, char *page, bool page_std); +extern void log_newpages(RelFileNode *rnode, ForkNumber forkNum, int num_pages, + BlockNumber *blknos, char **pages, bool page_std); extern XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std); extern void log_newpage_range(Relation rel, ForkNumber forkNum, BlockNumber startblk, BlockNumber endblk, bool page_std); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 0bbe0a122af..06ddb1f16b4 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,7 @@ */ /* yyyymmddN */ +/* FIXME: bump this before pushing! */ #define CATALOG_VERSION_NO 202009031 #endif diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883fc..a8e0c4ff8a5 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -480,6 +480,8 @@ amproc => 'gist_point_distance' }, { amprocfamily => 'gist/point_ops', amproclefttype => 'point', amprocrighttype => 'point', amprocnum => '9', amproc => 'gist_point_fetch' }, +{ amprocfamily => 'gist/point_ops', amproclefttype => 'point', + amprocrighttype => 'point', amprocnum => '11', amproc => 'gist_point_sortsupport' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', amproc => 'gist_box_consistent' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 687509ba926..96d7efd4270 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8062,6 +8062,9 @@ proname => 'gist_poly_distance', prorettype => 'float8', proargtypes => 'internal polygon int2 oid internal', prosrc => 'gist_poly_distance' }, +{ oid => '3435', descr => 'sort support', + proname => 'gist_point_sortsupport', prorettype => 'void', + proargtypes => 'internal', prosrc => 'gist_point_sortsupport' }, # GIN array support { oid => '2743', descr => 'GIN array support', diff --git a/src/include/utils/sortsupport.h b/src/include/utils/sortsupport.h index 264aec820b1..fb262c6e8d4 100644 --- a/src/include/utils/sortsupport.h +++ b/src/include/utils/sortsupport.h @@ -272,5 +272,6 @@ extern void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup); extern void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup); extern void PrepareSortSupportFromIndexRel(Relation indexRel, int16 strategy, SortSupport ssup); +extern void PrepareSortSupportFromGistIndexRel(Relation indexRel, SortSupport ssup); #endif /* SORTSUPPORT_H */ diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h index 9e76666fe94..c69b36e209a 100644 --- a/src/include/utils/tuplesort.h +++ b/src/include/utils/tuplesort.h @@ -217,6 +217,10 @@ extern Tuplesortstate *tuplesort_begin_index_hash(Relation heapRel, uint32 max_buckets, int workMem, SortCoordinate coordinate, bool randomAccess); +extern Tuplesortstate *tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + int workMem, SortCoordinate coordinate, + bool randomAccess); extern Tuplesortstate *tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, -- 2.20.1
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-09 16:50  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey M. Borodin @ 2020-09-09 16:50 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 9 сент. 2020 г., в 20:39, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> On 09/09/2020 15:20, Darafei "Komяpa" Praliaskouski wrote:
>> On Wed, Sep 9, 2020 at 3:09 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
>>> Come to think of it, the point z-order comparator could benefit a lot
>>> from key abbreviation, too. You could do the point -> zorder conversion
>>> in the abbreviation routine.
>> That's how it works in PostGIS, only that we moved to more
>> effecient Hilbert curve:
>> https://github.com/postgis/postgis/blob/54399b9f6b0f02e8db9444f9f042b8d4ca6d4fa4/postgis/lwgeom_btre...
> 
> Thanks, that's interesting.
> 
> I implemented the abbreviated keys for the point opclass, too, and noticed that the patch as it was never used it. I reworked the patch so that tuplesort_begin_index_gist() is responsible for looking up the sortsupport function, like tuplesort_begin_index_btree() does, and uses abbreviation when possible.
Wow, abbreviated sort made gist for points construction even 1.5x faster!
btw there is small typo in arg names in gist_bbox_zorder_cmp_abbrev(); z1,z2 -> a,b

> do we have regression test coverage for this?
Yes, sorting build for points is tested in point.sql, but with small dataset. index_including_gist.sql seems to be working with boxes, but triggers point paths too.

> , also on a SIZEOF_DATUM==4 system since the abbreviation works differently with that, and push if nothing new comes up. And clarify the documentation and/or comments that the sortsupport function sees "compressed" values.
> 
> I wonder if we could use sorting to also speed up building tsvector indexes? The values stored there are bit signatures, what would be a good sort order for those?
We need an order so that nearby values have a lot of bits in common.
What is the length of this signature?
For each 4 bytes we can compute number of 1s in it's binary representation. Then z-order these dwords as values 0-32.

This will be very inefficient grouping, but it will tend to keep empty and dense 4-byte regions apart.

Thanks for working on this!


Best regards, Andrey Borodin.





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-10 10:21  Oleg Bartunov <obartunov@postgrespro.ru>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  2 siblings, 1 reply; 111+ messages in thread

From: Oleg Bartunov @ 2020-09-10 10:21 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Pavel Borisov <pashkin.elfe@gmail.com>; Andrey M. Borodin <x4mmm@yandex-team.ru>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

On Mon, Sep 7, 2020 at 7:50 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
>
> On 07/09/2020 13:59, Pavel Borisov wrote:
> >>>> I suppose there is a big jump in integer value (whether signed or
> >>>> unsigned) as you cross from positive to negative floats, and then the
> >>>> sort order is reversed.  I have no idea if either of those things is a
> >>>> problem worth fixing.  That made me wonder if there might also be an
> >>
> >> I took a stab at fixing this, see attached patch (applies on top of your
> >> patch v14).
> >>
> >> To evaluate this, I used the other attached patch to expose the zorder
> >> function to SQL, and plotted points around zero with gnuplot. See the
> >> attached two images, one with patch v14, and the other one with this patch.
> >
> > I'd made testing of sorted SpGist build in cases of points distributed only
> > in 2d quadrant and points in all 4 quadrants and it appears that this
> > abnormality doesn't affect as much as Andrey supposed. But Heikki's patch
> > is really nice way to avoid what can be avoided and I'd like it is included
> > together with Andrey's patch.
>
> Thanks! Did you measure the quality of the built index somehow? The
> ordering shouldn't make any difference to the build speed, but it
> affects the shape of the resulting index and the speed of queries
> against it.
>
> I played with some simple queries like this:
>
> explain (analyze, buffers) select count(*) from points_good where p <@
> box(point(50, 50), point(75, 75));
>
> and looking at the "Buffers" line for how many pages were accessed.
> There doesn't seem to be any consistent difference between v14 and my
> fix. So I concur it doesn't seem to matter much.
>
>
> I played some more with plotting the curve. I wrote a little python
> program to make an animation of it, and also simulated how the points
> would be divided into pages, assuming that each GiST page can hold 200
> tuples (I think the real number is around 150 with default page size).
> In the animation, the leaf pages appear as rectangles as it walks
> through the Z-order curve. This is just a simulation by splitting all
> the points into batches of 200 and drawing a bounding box around each
> batch. I haven't checked the actual pages as the GiST creates, but I
> think this gives a good idea of how it works.

Heikki, you may use our gevel extension to visualize index tree
http://www.sai.msu.su/~megera/wiki/Gevel
I used it to investigate rtree index
http://www.sai.msu.su/~megera/wiki/Rtree_Index


>
> The animation shows that there's quite a lot of overlap between the
> pages. It's not necessarily this patch's business to try to improve
> that, and the non-sorting index build isn't perfect either. But it
> occurs to me that there's maybe one pretty simple trick we could do:
> instead of blindly filling the leaf pages in Z-order, collect tuples
> into a larger buffer, in Z-order. I'm thinking 32 pages worth of tuples,
> or something in that ballpark, or maybe go all the way up to work_mem.
> When the buffer fills up, call the picksplit code to divide the buffer
> into the actual pages, and flush them to disk. If you look at the
> animation and imagine that you would take a handful of pages in the
> order they're created, and re-divide the points with the split
> algorithm, there would be much less overlap.

Interesting to see also the size of index, it should be several times less.

>
> - Heikki



-- 
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-10 11:19  Pavel Borisov <pashkin.elfe@gmail.com>
  parent: Oleg Bartunov <obartunov@postgrespro.ru>
  0 siblings, 0 replies; 111+ messages in thread

From: Pavel Borisov @ 2020-09-10 11:19 UTC (permalink / raw)
  To: Oleg Bartunov <obartunov@postgrespro.ru>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Andrey M. Borodin <x4mmm@yandex-team.ru>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; pgsql-hackers

>
> Interesting to see also the size of index, it should be several times less.
>
> I've tested this above in CF thread and got ordered GiST index ~1.7 times
smaller than non-ordered one for single column of real points.


-- 
Best regards,
Pavel Borisov

Postgres Professional: http://postgrespro.com <http://www.postgrespro.com;

^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-10 12:43  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-10 12:43 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 09/09/2020 19:50, Andrey M. Borodin wrote:
>> 9 сент. 2020 г., в 20:39, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
>>
>> On 09/09/2020 15:20, Darafei "Komяpa" Praliaskouski wrote:
>>> On Wed, Sep 9, 2020 at 3:09 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
>>>> Come to think of it, the point z-order comparator could benefit a lot
>>>> from key abbreviation, too. You could do the point -> zorder conversion
>>>> in the abbreviation routine.
>>> That's how it works in PostGIS, only that we moved to more
>>> effecient Hilbert curve:
>>> https://github.com/postgis/postgis/blob/54399b9f6b0f02e8db9444f9f042b8d4ca6d4fa4/postgis/lwgeom_btre...
>>
>> Thanks, that's interesting.
>>
>> I implemented the abbreviated keys for the point opclass, too, and noticed that the patch as it was never used it. I reworked the patch so that tuplesort_begin_index_gist() is responsible for looking up the sortsupport function, like tuplesort_begin_index_btree() does, and uses abbreviation when possible.
> Wow, abbreviated sort made gist for points construction even 1.5x faster!
> btw there is small typo in arg names in gist_bbox_zorder_cmp_abbrev(); z1,z2 -> a,b

One more patch version attached. I fixed some memory leaks, and fixed 
the abbreviation on 32-bit systems, and a bunch of small comment changes 
and such.

- Heikki

Attachments:

v18-0001-Add-support-for-building-GiST-index-by-sorting.patch (text/x-patch, 46.3K) download From 79800152b6305e93129293452002cd082daadff4 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas <heikki.linnakangas@iki.fi> Date: Thu, 10 Sep 2020 15:37:08 +0300 Subject: [PATCH v18 1/1] Add support for building GiST index by sorting. This adds a new optional support function to the GiST access method: sortsupport. If it is defined, the GiST index is built by sorting all data to the order defined by the sortsupport's comparator function, and packing the tuples in that order to GiST pages. This is similar to how B-tree index build works, and is much faster than inserting the tuples one by one. The resulting index is smaller too, because the pages are packed more tightly, upto 'fillfactor'. The normal build method works by splitting pages, which tends to lead to more wasted space. The quality of the resulting index depends on how good the opclass-defined sort order is. A good order preserves locality of the input data. As the first user of this facility, add 'sortsupport' function to the point_ops opclass. It sorts the points in Z-order (aka Morton Code), by interleaving the bits of the X and Y coordinates. Author: Andrey Borodin Reviewed-by: Pavel Borisov, Thomas Munro Discussion: https://www.postgresql.org/message-id/1A36620E-CAD8-4267-9067-FB31385E7C0D%40yandex-team.ru --- doc/src/sgml/gist.sgml | 70 ++++ src/backend/access/gist/gistbuild.c | 506 ++++++++++++++++++++---- src/backend/access/gist/gistproc.c | 229 +++++++++++ src/backend/access/gist/gistutil.c | 53 ++- src/backend/access/gist/gistvalidate.c | 6 +- src/backend/access/transam/xloginsert.c | 57 +++ src/backend/utils/sort/sortsupport.c | 34 ++ src/backend/utils/sort/tuplesort.c | 57 +++ src/include/access/gist.h | 3 +- src/include/access/gist_private.h | 3 + src/include/access/xloginsert.h | 2 + src/include/catalog/catversion.h | 1 + src/include/catalog/pg_amproc.dat | 2 + src/include/catalog/pg_proc.dat | 3 + src/include/utils/sortsupport.h | 1 + src/include/utils/tuplesort.h | 4 + 16 files changed, 928 insertions(+), 103 deletions(-) diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml index f9226e7a35c..7c72a547409 100644 --- a/doc/src/sgml/gist.sgml +++ b/doc/src/sgml/gist.sgml @@ -259,6 +259,8 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); <function>compress</function> method is omitted. The optional tenth method <function>options</function> is needed if the operator class provides the user-specified parameters. + The <function>sortsupport</function> method is also optional and is used to + speed up building a <acronym>GiST</acronym> index. </para> <variablelist> @@ -1065,6 +1067,74 @@ my_compress(PG_FUNCTION_ARGS) </para> </listitem> </varlistentry> + + <varlistentry> + <term><function>sortsupport</function></term> + <listitem> + <para> + Returns a comparator function to sort data in a way that preserves + locality. It is used by <command>CREATE INDEX</command> and + <command>REINDEX</command>. The quality of the created index depends on + how well the sort order determined by the comparator function preserves + locality of the inputs. + </para> + <para> + The <function>sortsupport</function> method is optional. If it is not + provided, <command>CREATE INDEX</command> builds the index by inserting + each tuple to the tree using the <function>penalty</function> and + <function>picksplit</function> functions, which is much slower. + </para> + + <para> + The <acronym>SQL</acronym> declaration of the function must look like + this: + +<programlisting> +CREATE OR REPLACE FUNCTION my_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; +</programlisting> + + The argument is a pointer to a <structname>SortSupport</structname> + struct. At a minimum, the function must fill in its comparator field. + The comparator takes three arguments: two Datums to compare, and + a pointer to the <structname>SortSupport</structname> struct. The + Datums are the two indexed values in the format that they are stored + in the index; that is, in the format returned by the + <function>compress</function> method. The full API is defined in + <filename>src/include/utils/sortsupport.h</filename>. + </para> + + <para> + The matching code in the C module could then follow this skeleton: + +<programlisting> +PG_FUNCTION_INFO_V1(my_sortsupport); + +static int +my_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + /* establish order between x and y by computing some sorting value z */ + + int z1 = ComputeSpatialCode(x); + int z2 = ComputeSpatialCode(y); + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +my_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = my_fastcmp; + PG_RETURN_VOID(); +} +</programlisting> + </para> + </listitem> + </varlistentry> </variablelist> <para> diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index 671b5e9186f..5cbcc301009 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -3,6 +3,24 @@ * gistbuild.c * build algorithm for GiST indexes implementation. * + * There are two different strategies: + * + * 1. Sort all input tuples, pack them into GiST leaf pages in the sorted + * order, and create downlinks and internal pages as we go. This builds + * the index from the bottom up, similar to how B-tree index build + * works. + * + * 2. Start with an empty index, and insert all tuples one by one. + * + * The sorted method is used if the operator classes for all columns have + * a 'sortsupport' defined. Otherwise, we resort to the second strategy. + * + * The second strategy can optionally use buffers at different levels of + * the tree to reduce I/O, see "Buffering build algorithm" in the README + * for a more detailed explanation. It initially calls insert over and + * over, but switches to the buffered algorithm after a certain number of + * tuples (unless buffering mode is disabled). + * * * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -28,6 +46,7 @@ #include "storage/smgr.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/tuplesort.h" /* Step of index tuples for check whether to switch to buffering build mode */ #define BUFFERING_MODE_SWITCH_CHECK_STEP 256 @@ -40,8 +59,14 @@ */ #define BUFFERING_MODE_TUPLE_SIZE_STATS_TARGET 4096 +/* + * Strategy used to build the index. It can change between the + * GIST_BUFFERING_* modes on the fly, but if the Sorted method is used, + * that needs to be decided up-front and cannot be changed afterwards. + */ typedef enum { + GIST_SORTED_BUILD, /* bottom-up build by sorting */ GIST_BUFFERING_DISABLED, /* in regular build mode and aren't going to * switch */ GIST_BUFFERING_AUTO, /* in regular build mode, but will switch to @@ -51,7 +76,7 @@ typedef enum * before switching to the buffering build * mode */ GIST_BUFFERING_ACTIVE /* in buffering build mode */ -} GistBufferingMode; +} GistBuildMode; /* Working state for gistbuild and its callback */ typedef struct @@ -60,23 +85,58 @@ typedef struct Relation heaprel; GISTSTATE *giststate; - int64 indtuples; /* number of tuples indexed */ - int64 indtuplesSize; /* total size of all indexed tuples */ - Size freespace; /* amount of free space to leave on pages */ + GistBuildMode buildMode; + + int64 indtuples; /* number of tuples indexed */ + /* * Extra data structures used during a buffering build. 'gfbb' contains * information related to managing the build buffers. 'parentMap' is a * lookup table of the parent of each internal page. */ + int64 indtuplesSize; /* total size of all indexed tuples */ GISTBuildBuffers *gfbb; HTAB *parentMap; - GistBufferingMode bufferingMode; + /* + * Extra data structures used during a sorting build. + */ + Tuplesortstate *sortstate; /* state data for tuplesort.c */ + + BlockNumber pages_allocated; + BlockNumber pages_written; + + int ready_num_pages; + BlockNumber ready_blknos[XLR_MAX_BLOCK_ID]; + Page ready_pages[XLR_MAX_BLOCK_ID]; } GISTBuildState; +/* + * In sorted build, we use a stack of these structs, one for each level, + * to hold an in-memory buffer of the righmost page at the level. When the + * page fills up, it is written out and a new page is allocated. + */ +typedef struct GistSortedBuildPageState +{ + Page page; + struct GistSortedBuildPageState *parent; /* Upper level, if any */ +} GistSortedBuildPageState; + /* prototypes for private functions */ + +static void gistSortedBuildCallback(Relation index, ItemPointer tid, + Datum *values, bool *isnull, + bool tupleIsAlive, void *state); +static void gist_indexsortbuild(GISTBuildState *state); +static void gist_indexsortbuild_pagestate_add(GISTBuildState *state, + GistSortedBuildPageState *pagestate, + IndexTuple itup); +static void gist_indexsortbuild_pagestate_flush(GISTBuildState *state, + GistSortedBuildPageState *pagestate); +static void gist_indexsortbuild_flush_ready_pages(GISTBuildState *state); + static void gistInitBuffering(GISTBuildState *buildstate); static int calculatePagesPerBuffer(GISTBuildState *buildstate, int levelStep); static void gistBuildCallback(Relation index, @@ -107,10 +167,9 @@ static void gistMemorizeParent(GISTBuildState *buildstate, BlockNumber child, static void gistMemorizeAllDownlinks(GISTBuildState *buildstate, Buffer parent); static BlockNumber gistGetParent(GISTBuildState *buildstate, BlockNumber child); + /* - * Main entry point to GiST index build. Initially calls insert over and over, - * but switches to more efficient buffering build algorithm after a certain - * number of tuples (unless buffering mode is disabled). + * Main entry point to GiST index build. */ IndexBuildResult * gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) @@ -118,124 +177,403 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) IndexBuildResult *result; double reltuples; GISTBuildState buildstate; - Buffer buffer; - Page page; MemoryContext oldcxt = CurrentMemoryContext; int fillfactor; + Oid SortSupportFnOids[INDEX_MAX_KEYS]; + bool hasallsortsupports; + int keyscount = IndexRelationGetNumberOfKeyAttributes(index); + GiSTOptions *options = NULL; + + /* + * We expect to be called exactly once for any index relation. If that's + * not the case, big trouble's what we have. + */ + if (RelationGetNumberOfBlocks(index) != 0) + elog(ERROR, "index \"%s\" already contains data", + RelationGetRelationName(index)); + + if (index->rd_options) + options = (GiSTOptions *) index->rd_options; buildstate.indexrel = index; buildstate.heaprel = heap; + buildstate.sortstate = NULL; + buildstate.giststate = initGISTstate(index); - if (index->rd_options) + /* + * Create a temporary memory context that is reset once for each tuple + * processed. (Note: we don't bother to make this a child of the + * giststate's scanCxt, so we have to delete it separately at the end.) + */ + buildstate.giststate->tempCxt = createTempGistContext(); + + /* + * Choose build strategy. If all keys support sorting, do that. Otherwise + * the default strategy is switch to buffering mode when the index grows + * too large to fit in cache. + */ + hasallsortsupports = true; + for (int i = 0; i < keyscount; i++) { - /* Get buffering mode from the options string */ - GiSTOptions *options = (GiSTOptions *) index->rd_options; + SortSupportFnOids[i] = index_getprocid(index, i + 1, + GIST_SORTSUPPORT_PROC); + if (!OidIsValid(SortSupportFnOids[i])) + { + hasallsortsupports = false; + break; + } + } + if (hasallsortsupports) + { + buildstate.buildMode = GIST_SORTED_BUILD; + } + else if (options) + { if (options->buffering_mode == GIST_OPTION_BUFFERING_ON) - buildstate.bufferingMode = GIST_BUFFERING_STATS; + buildstate.buildMode = GIST_BUFFERING_STATS; else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF) - buildstate.bufferingMode = GIST_BUFFERING_DISABLED; + buildstate.buildMode = GIST_BUFFERING_DISABLED; else - buildstate.bufferingMode = GIST_BUFFERING_AUTO; - - fillfactor = options->fillfactor; + buildstate.buildMode = GIST_BUFFERING_AUTO; } else { - /* - * By default, switch to buffering mode when the index grows too large - * to fit in cache. - */ - buildstate.bufferingMode = GIST_BUFFERING_AUTO; - fillfactor = GIST_DEFAULT_FILLFACTOR; + buildstate.buildMode = GIST_BUFFERING_AUTO; } - /* Calculate target amount of free space to leave on pages */ + + /* + * Calculate target amount of free space to leave on pages. + */ + fillfactor = options ? options->fillfactor : GIST_DEFAULT_FILLFACTOR; buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100; /* - * We expect to be called exactly once for any index relation. If that's - * not the case, big trouble's what we have. + * Build the index using the chosen strategy. */ - if (RelationGetNumberOfBlocks(index) != 0) - elog(ERROR, "index \"%s\" already contains data", - RelationGetRelationName(index)); + buildstate.indtuples = 0; + buildstate.indtuplesSize = 0; - /* no locking is needed */ - buildstate.giststate = initGISTstate(index); + if (buildstate.buildMode == GIST_SORTED_BUILD) + { + /* + * Sort all data, build the index from bottom up. + */ + buildstate.sortstate = tuplesort_begin_index_gist(heap, + index, + maintenance_work_mem, + NULL, + false); + + /* Scan the table, adding all tuples to the tuplesort */ + reltuples = table_index_build_scan(heap, index, indexInfo, true, true, + gistSortedBuildCallback, + (void *) &buildstate, NULL); + + /* + * Perform the sort and build index pages. + */ + tuplesort_performsort(buildstate.sortstate); + + gist_indexsortbuild(&buildstate); + + tuplesort_end(buildstate.sortstate); + } + else + { + /* + * Initialize an empty index and insert all tuples, possibly using + * buffers on intermediate levels. + */ + Buffer buffer; + Page page; + + /* initialize the root page */ + buffer = gistNewBuffer(index); + Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO); + page = BufferGetPage(buffer); + + START_CRIT_SECTION(); + + GISTInitBuffer(buffer, F_LEAF); + + MarkBufferDirty(buffer); + PageSetLSN(page, GistBuildLSN); + + UnlockReleaseBuffer(buffer); + + END_CRIT_SECTION(); + + /* Scan the table, inserting all the tuples to the index. */ + reltuples = table_index_build_scan(heap, index, indexInfo, true, true, + gistBuildCallback, + (void *) &buildstate, NULL); + + /* + * If buffering was used, flush out all the tuples that are still in + * the buffers. + */ + if (buildstate.buildMode == GIST_BUFFERING_ACTIVE) + { + elog(DEBUG1, "all tuples processed, emptying buffers"); + gistEmptyAllBuffers(&buildstate); + gistFreeBuildBuffers(buildstate.gfbb); + } + + /* + * We didn't write WAL records as we built the index, so if + * WAL-logging is required, write all pages to the WAL now. + */ + if (RelationNeedsWAL(index)) + { + log_newpage_range(index, MAIN_FORKNUM, + 0, RelationGetNumberOfBlocks(index), + true); + } + } + + /* okay, all heap tuples are indexed */ + MemoryContextSwitchTo(oldcxt); + MemoryContextDelete(buildstate.giststate->tempCxt); + + freeGISTstate(buildstate.giststate); /* - * Create a temporary memory context that is reset once for each tuple - * processed. (Note: we don't bother to make this a child of the - * giststate's scanCxt, so we have to delete it separately at the end.) + * Return statistics */ - buildstate.giststate->tempCxt = createTempGistContext(); + result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); - /* initialize the root page */ - buffer = gistNewBuffer(index); - Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO); - page = BufferGetPage(buffer); + result->heap_tuples = reltuples; + result->index_tuples = (double) buildstate.indtuples; + + return result; +} + +/*------------------------------------------------------------------------- + * Routines for sorted build + *------------------------------------------------------------------------- + */ + +/* + * Per-tuple callback for table_index_build_scan. + */ +static void +gistSortedBuildCallback(Relation index, + ItemPointer tid, + Datum *values, + bool *isnull, + bool tupleIsAlive, + void *state) +{ + GISTBuildState *buildstate = (GISTBuildState *) state; + MemoryContext oldCtx; + Datum compressed_values[INDEX_MAX_KEYS]; - START_CRIT_SECTION(); + oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); - GISTInitBuffer(buffer, F_LEAF); + /* Form an index tuple and point it at the heap tuple */ + gistCompressValues(buildstate->giststate, index, + values, isnull, + true, compressed_values); - MarkBufferDirty(buffer); - PageSetLSN(page, GistBuildLSN); + tuplesort_putindextuplevalues(buildstate->sortstate, + buildstate->indexrel, + tid, + compressed_values, isnull); - UnlockReleaseBuffer(buffer); + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(buildstate->giststate->tempCxt); - END_CRIT_SECTION(); + /* Update tuple count. */ + buildstate->indtuples += 1; +} - /* build the index */ - buildstate.indtuples = 0; - buildstate.indtuplesSize = 0; +/* + * Build GiST index from bottom up from pre-sorted tuples. + */ +static void +gist_indexsortbuild(GISTBuildState *state) +{ + IndexTuple itup; + GistSortedBuildPageState *leafstate; + GistSortedBuildPageState *pagestate; + Page page; + + state->pages_allocated = 0; + state->pages_written = 0; + state->ready_num_pages = 0; /* - * Do the heap scan. + * Write an empty page as a placeholder for the root page. It will be + * replaced with the real root page at the end. */ - reltuples = table_index_build_scan(heap, index, indexInfo, true, true, - gistBuildCallback, - (void *) &buildstate, NULL); + page = palloc0(BLCKSZ); + smgrextend(state->indexrel->rd_smgr, MAIN_FORKNUM, GIST_ROOT_BLKNO, + page, true); + state->pages_allocated++; + state->pages_written++; + + /* Allocate a temporary buffer for the first leaf page. */ + leafstate = palloc(sizeof(GistSortedBuildPageState)); + leafstate->page = page; + leafstate->parent = NULL; + gistinitpage(page, F_LEAF); /* - * If buffering was used, flush out all the tuples that are still in the - * buffers. + * Fill index pages with tuples in the sorted order. */ - if (buildstate.bufferingMode == GIST_BUFFERING_ACTIVE) + while ((itup = tuplesort_getindextuple(state->sortstate, true)) != NULL) { - elog(DEBUG1, "all tuples processed, emptying buffers"); - gistEmptyAllBuffers(&buildstate); - gistFreeBuildBuffers(buildstate.gfbb); + gist_indexsortbuild_pagestate_add(state, leafstate, itup); + MemoryContextReset(state->giststate->tempCxt); } - /* okay, all heap tuples are indexed */ - MemoryContextSwitchTo(oldcxt); - MemoryContextDelete(buildstate.giststate->tempCxt); - - freeGISTstate(buildstate.giststate); - /* - * We didn't write WAL records as we built the index, so if WAL-logging is - * required, write all pages to the WAL now. + * Write out the partially full non-root pages. + * + * Keep in mind that flush can build a new root. */ - if (RelationNeedsWAL(index)) + pagestate = leafstate; + while (pagestate->parent != NULL) { - log_newpage_range(index, MAIN_FORKNUM, - 0, RelationGetNumberOfBlocks(index), - true); + GistSortedBuildPageState *parent; + + gist_indexsortbuild_pagestate_flush(state, pagestate); + parent = pagestate->parent; + pfree(pagestate->page); + pfree(pagestate); + pagestate = parent; } + gist_indexsortbuild_flush_ready_pages(state); + + /* Write out the root */ + smgrwrite(state->indexrel->rd_smgr, MAIN_FORKNUM, GIST_ROOT_BLKNO, + pagestate->page, true); + if (RelationNeedsWAL(state->indexrel)) + log_newpage(&state->indexrel->rd_node, MAIN_FORKNUM, GIST_ROOT_BLKNO, + pagestate->page, true); + + pfree(pagestate->page); + pfree(pagestate); +} + +/* + * Add tuple to a page. If the pages is full, write it out and re-initialize + * a new page first. + */ +static void +gist_indexsortbuild_pagestate_add(GISTBuildState *state, + GistSortedBuildPageState * pagestate, + IndexTuple itup) +{ + Page page = pagestate->page; + Size sizeNeeded; + + /* Does the tuple fit? If not, flush */ + sizeNeeded = IndexTupleSize(itup) + sizeof(ItemIdData) + state->freespace; + if (PageGetFreeSpace(page) < sizeNeeded) + gist_indexsortbuild_pagestate_flush(state, pagestate); + + gistfillbuffer(page, &itup, 1, InvalidOffsetNumber); +} + +static void +gist_indexsortbuild_pagestate_flush(GISTBuildState *state, + GistSortedBuildPageState * pagestate) +{ + GistSortedBuildPageState *parent; + IndexTuple *itvec; + IndexTuple union_tuple; + int vect_len; + bool isleaf; + BlockNumber blkno; + MemoryContext oldCtx; + + /* check once per page */ + CHECK_FOR_INTERRUPTS(); + + if (state->ready_num_pages == XLR_MAX_BLOCK_ID) + gist_indexsortbuild_flush_ready_pages(state); + /* - * Return statistics + * The page is now complete. Assign a block number to it, and add it to + * the list of finished pages. (We don't write it out immediately, because + * we want to WAL-log the pages in batches.) */ - result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); + blkno = state->pages_allocated++; + state->ready_blknos[state->ready_num_pages] = blkno; + state->ready_pages[state->ready_num_pages] = pagestate->page; + state->ready_num_pages++; - result->heap_tuples = reltuples; - result->index_tuples = (double) buildstate.indtuples; + isleaf = GistPageIsLeaf(pagestate->page); - return result; + /* + * Form a downlink tuple to represent all the tuples on the page. + */ + oldCtx = MemoryContextSwitchTo(state->giststate->tempCxt); + itvec = gistextractpage(pagestate->page, &vect_len); + union_tuple = gistunion(state->indexrel, itvec, vect_len, + state->giststate); + ItemPointerSetBlockNumber(&(union_tuple->t_tid), blkno); + MemoryContextSwitchTo(oldCtx); + + /* + * Insert the downlink to the parent page. If this was the root, create a + * new page as the parent, which becomes the new root. + */ + parent = pagestate->parent; + if (parent == NULL) + { + parent = palloc(sizeof(GistSortedBuildPageState)); + parent->page = (Page) palloc(BLCKSZ); + parent->parent = NULL; + gistinitpage(parent->page, 0); + + pagestate->parent = parent; + } + gist_indexsortbuild_pagestate_add(state, parent, union_tuple); + + /* Re-initialize the page buffer for next page on this level. */ + pagestate->page = palloc(BLCKSZ); + gistinitpage(pagestate->page, isleaf ? F_LEAF : 0); +} + +static void +gist_indexsortbuild_flush_ready_pages(GISTBuildState *state) +{ + if (state->ready_num_pages == 0) + return; + + for (int i = 0; i < state->ready_num_pages; i++) + { + /* Currently, the blocks must be buffered in order. */ + if (state->ready_blknos[i] != state->pages_written) + elog(ERROR, "unexpected block number to flush GiST sorting build"); + + smgrextend(state->indexrel->rd_smgr, + MAIN_FORKNUM, + state->pages_written++, + state->ready_pages[i], + true); + } + + if (RelationNeedsWAL(state->indexrel)) + log_newpages(&state->indexrel->rd_node, MAIN_FORKNUM, state->ready_num_pages, + state->ready_blknos, state->ready_pages, true); + + for (int i = 0; i < state->ready_num_pages; i++) + pfree(state->ready_pages[i]); + + state->ready_num_pages = 0; } + +/*------------------------------------------------------------------------- + * Routines for non-sorted build + *------------------------------------------------------------------------- + */ + /* * Attempt to switch to buffering mode. * @@ -375,7 +713,7 @@ gistInitBuffering(GISTBuildState *buildstate) if (levelStep <= 0) { elog(DEBUG1, "failed to switch to buffered GiST build"); - buildstate->bufferingMode = GIST_BUFFERING_DISABLED; + buildstate->buildMode = GIST_BUFFERING_DISABLED; return; } @@ -392,7 +730,7 @@ gistInitBuffering(GISTBuildState *buildstate) gistInitParentMap(buildstate); - buildstate->bufferingMode = GIST_BUFFERING_ACTIVE; + buildstate->buildMode = GIST_BUFFERING_ACTIVE; elog(DEBUG1, "switched to buffered GiST build; level step = %d, pagesPerBuffer = %d", levelStep, pagesPerBuffer); @@ -453,10 +791,12 @@ gistBuildCallback(Relation index, oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); /* form an index tuple and point it at the heap tuple */ - itup = gistFormTuple(buildstate->giststate, index, values, isnull, true); + itup = gistFormTuple(buildstate->giststate, index, + values, isnull, + true); itup->t_tid = *tid; - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) + if (buildstate->buildMode == GIST_BUFFERING_ACTIVE) { /* We have buffers, so use them. */ gistBufferingBuildInsert(buildstate, itup); @@ -478,7 +818,7 @@ gistBuildCallback(Relation index, MemoryContextSwitchTo(oldCtx); MemoryContextReset(buildstate->giststate->tempCxt); - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE && + if (buildstate->buildMode == GIST_BUFFERING_ACTIVE && buildstate->indtuples % BUFFERING_MODE_TUPLE_SIZE_STATS_TARGET == 0) { /* Adjust the target buffer size now */ @@ -493,10 +833,10 @@ gistBuildCallback(Relation index, * To avoid excessive calls to smgrnblocks(), only check this every * BUFFERING_MODE_SWITCH_CHECK_STEP index tuples */ - if ((buildstate->bufferingMode == GIST_BUFFERING_AUTO && + if ((buildstate->buildMode == GIST_BUFFERING_AUTO && buildstate->indtuples % BUFFERING_MODE_SWITCH_CHECK_STEP == 0 && effective_cache_size < smgrnblocks(index->rd_smgr, MAIN_FORKNUM)) || - (buildstate->bufferingMode == GIST_BUFFERING_STATS && + (buildstate->buildMode == GIST_BUFFERING_STATS && buildstate->indtuples >= BUFFERING_MODE_TUPLE_SIZE_STATS_TARGET)) { /* diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 9ace64c3c4a..d7afc8caf26 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -24,6 +24,7 @@ #include "utils/builtins.h" #include "utils/float.h" #include "utils/geo_decls.h" +#include "utils/sortsupport.h" static bool gist_box_leaf_consistent(BOX *key, BOX *query, @@ -31,6 +32,15 @@ static bool gist_box_leaf_consistent(BOX *key, BOX *query, static bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy); +static uint64 point_zorder_internal(float4 x, float4 y); +static uint64 part_bits32_by2(uint32 x); +static uint32 ieee_float32_to_uint32(float f); +static int gist_bbox_zorder_cmp(Datum a, Datum b, SortSupport ssup); +static Datum gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup); +static int gist_bbox_zorder_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup); +static bool gist_bbox_zorder_abbrev_abort(int memtupcount, SortSupport ssup); + + /* Minimum accepted ratio of split */ #define LIMIT_RATIO 0.3 @@ -1540,3 +1550,222 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } + +/* + * Z-order routines for fast index build + */ + +/* + * Compute Z-value of a point + * + * Z-order (also known as Morton Code) maps a two-dimensional point to a + * single integer, in a way that preserves locality. Points that are close in + * the two-dimensional space are mapped to integer that are not far from each + * other. We do that by interleaving the bits in the X and Y components. + * + * Morton Code is normally defined only for integers, but the X and Y values + * of a point are floating point. We expect floats to be in IEEE format. + */ +static uint64 +point_zorder_internal(float4 x, float4 y) +{ + uint32 ix = ieee_float32_to_uint32(x); + uint32 iy = ieee_float32_to_uint32(y); + + /* Interleave the bits */ + return part_bits32_by2(ix) | (part_bits32_by2(iy) << 1); +} + +/* Interleave 32 bits with zeroes */ +static uint64 +part_bits32_by2(uint32 x) +{ + uint64 n = x; + + n = (n | (n << 16)) & UINT64CONST(0x0000FFFF0000FFFF); + n = (n | (n << 8)) & UINT64CONST(0x00FF00FF00FF00FF); + n = (n | (n << 4)) & UINT64CONST(0x0F0F0F0F0F0F0F0F); + n = (n | (n << 2)) & UINT64CONST(0x3333333333333333); + n = (n | (n << 1)) & UINT64CONST(0x5555555555555555); + + return n; +} + +/* + * Convert a 32-bit IEEE float to uint32 in a way that preserves the ordering + */ +static uint32 +ieee_float32_to_uint32(float f) +{ + /*---- + * + * IEEE 754 floating point format + * ------------------------------ + * + * IEEE 754 floating point numbers have this format: + * + * exponent (8 bits) + * | + * s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm + * | | + * sign mantissa (23 bits) + * + * Infinity has all bits in the exponent set and the mantissa is all + * zeros. Negative infinity is the same but with the sign bit set. + * + * NaNs are represented with all bits in the exponent set, and the least + * significant bit in the mantissa also set. The rest of the mantissa bits + * can be used to distinguish different kinds of NaNs. + * + * The IEEE format has the nice property that when you take the bit + * representation and interpret it as an integer, the order is preserved, + * except for the sign. That holds for the +-Infinity values too. + * + * Mapping to uint32 + * ----------------- + * + * In order to have a smooth transition from negative to positive numbers, + * we map floats to unsigned integers like this: + * + * x < 0 to range 0-7FFFFFFF + * x = 0 to value 8000000 (both positive and negative zero) + * x > 0 to range 8000001-FFFFFFFF + * + * We don't care to distinguish different kind of NaNs, so they are all + * mapped to the same arbitrary value, FFFFFFFF. Because of the IEEE bit + * representation of NaNs, there aren't any non-NaN values that would be + * mapped to FFFFFFFF. In fact, there is a range of unused values on both + * ends of the uint32 space. + */ + if (isnan(f)) + return 0xFFFFFFFF; + else + { + union + { + float f; + uint32 i; + } u; + + u.f = f; + + /* Check the sign bit */ + if ((u.i & 0x80000000) != 0) + { + /* + * Map the negative value to range 0-7FFFFFFF. This flips the sign + * bit to 0 in the same instruction. + */ + Assert(f < 0); + u.i ^= 0xFFFFFFFF; + } + else + { + /* Map the positive value (or 0) to range 80000000-FFFFFFFF */ + u.i |= 0x80000000; + } + + return u.i; + } +} + +/* + * Compare the Z-order of points + */ +static int +gist_bbox_zorder_cmp(Datum a, Datum b, SortSupport ssup) +{ + Point *p1 = &(DatumGetBoxP(a)->low); + Point *p2 = &(DatumGetBoxP(b)->low); + uint64 z1; + uint64 z2; + + /* + * Do a quick check for equality first. It's not clear if this is worth it + * in general, but certainly is when used as tie-breaker with abbreviated + * keys, + */ + if (p1->x == p2->x && p1->y == p2->y) + return 0; + + z1 = point_zorder_internal(p1->x, p1->y); + z2 = point_zorder_internal(p2->x, p2->y); + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * Abbreviated version of Z-order comparison + * + * The abbreviated format is a Z-order value computed from the two 32-bit + * floats. If SIZEOF_DATUM == 8, the 64-bit Z-order value fits fully in the + * abbreviated Datum, otherwise use its most significant bits. + */ +static Datum +gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup) +{ + Point *p = &(DatumGetBoxP(original)->low); + uint64 z; + + z = point_zorder_internal(p->x, p->y); + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gist_bbox_zorder_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + /* + * Compare the pre-computed Z-orders as unsigned integers. Datum is a + * typedef for 'uintptr_t', so no casting is required. + */ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + * + * On 64-bit systems, the abbreviation is not lossy so it is always + * worthwhile. (Perhaps it's not on 32-bit systems, but we don't bother + * with logic to decide.) + */ +static bool +gist_bbox_zorder_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gist_point_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gist_bbox_zorder_cmp_abbrev; + ssup->abbrev_converter = gist_bbox_zorder_abbrev_convert; + ssup->abbrev_abort = gist_bbox_zorder_abbrev_abort; + ssup->abbrev_full_comparator = gist_bbox_zorder_cmp; + } + else + { + ssup->comparator = gist_bbox_zorder_cmp; + } + PG_RETURN_VOID(); +} diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 0516059e3dd..615b5ade233 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -572,12 +572,31 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, - Datum attdata[], bool isnull[], bool isleaf) + Datum *attdata, bool *isnull, bool isleaf) { Datum compatt[INDEX_MAX_KEYS]; - int i; IndexTuple res; + gistCompressValues(giststate, r, attdata, isnull, isleaf, compatt); + + res = index_form_tuple(isleaf ? giststate->leafTupdesc : + giststate->nonLeafTupdesc, + compatt, isnull); + + /* + * The offset number on tuples on internal pages is unused. For historical + * reasons, it is set to 0xffff. + */ + ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff); + return res; +} + +void +gistCompressValues(GISTSTATE *giststate, Relation r, + Datum *attdata, bool *isnull, bool isleaf, Datum *compatt) +{ + int i; + /* * Call the compress method on each attribute. */ @@ -617,17 +636,6 @@ gistFormTuple(GISTSTATE *giststate, Relation r, compatt[i] = attdata[i]; } } - - res = index_form_tuple(isleaf ? giststate->leafTupdesc : - giststate->nonLeafTupdesc, - compatt, isnull); - - /* - * The offset number on tuples on internal pages is unused. For historical - * reasons, it is set to 0xffff. - */ - ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff); - return res; } /* @@ -745,14 +753,11 @@ gistpenalty(GISTSTATE *giststate, int attno, * Initialize a new index page */ void -GISTInitBuffer(Buffer b, uint32 f) +gistinitpage(Page page, uint32 f) { GISTPageOpaque opaque; - Page page; - Size pageSize; + Size pageSize = BLCKSZ; - pageSize = BufferGetPageSize(b); - page = BufferGetPage(b); PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); opaque = GistPageGetOpaque(page); @@ -763,6 +768,18 @@ GISTInitBuffer(Buffer b, uint32 f) opaque->gist_page_id = GIST_PAGE_ID; } +/* + * Initialize a new index buffer + */ +void +GISTInitBuffer(Buffer b, uint32 f) +{ + Page page; + + page = BufferGetPage(b); + gistinitpage(page, f); +} + /* * Verify that a freshly-read page looks sane. */ diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index 2b9ab693be1..8a14620fab2 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -143,6 +143,10 @@ gistvalidate(Oid opclassoid) case GIST_OPTIONS_PROC: ok = check_amoptsproc_signature(procform->amproc); break; + case GIST_SORTSUPPORT_PROC: + ok = check_amproc_signature(procform->amproc, VOIDOID, true, + 1, 1, INTERNALOID); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -263,7 +267,7 @@ gistvalidate(Oid opclassoid) continue; /* got it */ if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC || i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC || - i == GIST_OPTIONS_PROC) + i == GIST_OPTIONS_PROC || i == GIST_SORTSUPPORT_PROC) continue; /* optional methods */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c index c526bb19281..1f0e4e01e69 100644 --- a/src/backend/access/transam/xloginsert.c +++ b/src/backend/access/transam/xloginsert.c @@ -1019,6 +1019,63 @@ log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno, return recptr; } +/* + * Like log_newpage(), but allows logging multiple pages in one operation. + * It is more efficient than calling log_newpage() for each page separately, + * because we can write multiple pages in a single WAL record. + */ +void +log_newpages(RelFileNode *rnode, ForkNumber forkNum, int num_pages, + BlockNumber *blknos, Page *pages, bool page_std) +{ + int flags; + XLogRecPtr recptr; + int i; + int j; + + flags = REGBUF_FORCE_IMAGE; + if (page_std) + flags |= REGBUF_STANDARD; + + /* + * Iterate over all the pages. They are collected into batches of + * XLR_MAX_BLOCK_ID pages, and a single WAL-record is written for each + * batch. + */ + XLogEnsureRecordSpace(XLR_MAX_BLOCK_ID - 1, 0); + + i = 0; + while (i < num_pages) + { + int batch_start = i; + int nbatch; + + XLogBeginInsert(); + + nbatch = 0; + while (nbatch < XLR_MAX_BLOCK_ID && i < num_pages) + { + XLogRegisterBlock(nbatch, rnode, forkNum, blknos[i], pages[i], flags); + i++; + nbatch++; + } + + recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI); + + for (j = batch_start; j < i; j++) + { + /* + * The page may be uninitialized. If so, we can't set the LSN because that + * would corrupt the page. + */ + if (!PageIsNew(pages[j])) + { + PageSetLSN(pages[j], recptr); + } + } + } +} + /* * Write a WAL record containing a full image of a page. * diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c index fcfe6e831a1..c436fbb4ce1 100644 --- a/src/backend/utils/sort/sortsupport.c +++ b/src/backend/utils/sort/sortsupport.c @@ -15,6 +15,7 @@ #include "postgres.h" +#include "access/gist.h" #include "access/nbtree.h" #include "catalog/pg_am.h" #include "fmgr.h" @@ -175,3 +176,36 @@ PrepareSortSupportFromIndexRel(Relation indexRel, int16 strategy, FinishSortSupportFunction(opfamily, opcintype, ssup); } + +/* + * Fill in SortSupport given a GiST index relation + * + * Caller must previously have zeroed the SortSupportData structure and then + * filled in ssup_cxt, ssup_attno, ssup_collation, and ssup_nulls_first. This + * will fill in ssup_reverse (always false for GiST index build), as well as + * the comparator function pointer. + */ +void +PrepareSortSupportFromGistIndexRel(Relation indexRel, SortSupport ssup) +{ + Oid opfamily = indexRel->rd_opfamily[ssup->ssup_attno - 1]; + Oid opcintype = indexRel->rd_opcintype[ssup->ssup_attno - 1]; + Oid sortSupportFunction; + + Assert(ssup->comparator == NULL); + + if (indexRel->rd_rel->relam != GIST_AM_OID) + elog(ERROR, "unexpected non-gist AM: %u", indexRel->rd_rel->relam); + ssup->ssup_reverse = false; + + /* + * Look up the sort support function. This is simpler than for B-tree + * indexes because we don't support the old-style btree comparators. + */ + sortSupportFunction = get_opfamily_proc(opfamily, opcintype, opcintype, + GIST_SORTSUPPORT_PROC); + if (!OidIsValid(sortSupportFunction)) + elog(ERROR, "missing support function %d(%u,%u) in opfamily %u", + GIST_SORTSUPPORT_PROC, opcintype, opcintype, opfamily); + OidFunctionCall1(sortSupportFunction, PointerGetDatum(ssup)); +} diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 3c49476483b..fb4ac4d2f9f 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -1167,6 +1167,63 @@ tuplesort_begin_index_hash(Relation heapRel, return state; } +Tuplesortstate * +tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + int workMem, + SortCoordinate coordinate, + bool randomAccess) +{ + Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, + randomAccess); + MemoryContext oldcontext; + int i; + + oldcontext = MemoryContextSwitchTo(state->sortcontext); + +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "begin index sort: workMem = %d, randomAccess = %c", + workMem, randomAccess ? 't' : 'f'); +#endif + + state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); + + state->comparetup = comparetup_index_btree; + state->copytup = copytup_index; + state->writetup = writetup_index; + state->readtup = readtup_index; + + state->heapRel = heapRel; + state->indexRel = indexRel; + + /* Prepare SortSupport data for each column */ + state->sortKeys = (SortSupport) palloc0(state->nKeys * + sizeof(SortSupportData)); + + for (i = 0; i < state->nKeys; i++) + { + SortSupport sortKey = state->sortKeys + i; + + sortKey->ssup_cxt = CurrentMemoryContext; + sortKey->ssup_collation = indexRel->rd_indcollation[i]; + sortKey->ssup_nulls_first = false; + sortKey->ssup_attno = i + 1; + /* Convey if abbreviation optimization is applicable in principle */ + sortKey->abbreviate = (i == 0); + + AssertState(sortKey->ssup_attno != 0); + + /* Look for a sort support function */ + PrepareSortSupportFromGistIndexRel(indexRel, sortKey); + } + + MemoryContextSwitchTo(oldcontext); + + return state; +} + Tuplesortstate * tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 4994351697c..4f6dae9a76b 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -37,7 +37,8 @@ #define GIST_DISTANCE_PROC 8 #define GIST_FETCH_PROC 9 #define GIST_OPTIONS_PROC 10 -#define GISTNProcs 10 +#define GIST_SORTSUPPORT_PROC 11 +#define GISTNProcs 11 /* * Page opaque data in a GiST index page. diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 02e985549f6..b68c01a5f24 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -501,12 +501,15 @@ extern IndexTuple gistgetadjusted(Relation r, GISTSTATE *giststate); extern IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf); +extern void gistCompressValues(GISTSTATE *giststate, Relation r, + Datum *attdata, bool *isnull, bool isleaf, Datum *compatt); extern OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate); extern void GISTInitBuffer(Buffer b, uint32 f); +extern void gistinitpage(Page page, uint32 f); extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull); diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h index 63df25ae90f..4ba2c56be60 100644 --- a/src/include/access/xloginsert.h +++ b/src/include/access/xloginsert.h @@ -54,6 +54,8 @@ extern bool XLogCheckBufferNeedsBackup(Buffer buffer); extern XLogRecPtr log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blk, char *page, bool page_std); +extern void log_newpages(RelFileNode *rnode, ForkNumber forkNum, int num_pages, + BlockNumber *blknos, char **pages, bool page_std); extern XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std); extern void log_newpage_range(Relation rel, ForkNumber forkNum, BlockNumber startblk, BlockNumber endblk, bool page_std); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 0bbe0a122af..06ddb1f16b4 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,7 @@ */ /* yyyymmddN */ +/* FIXME: bump this before pushing! */ #define CATALOG_VERSION_NO 202009031 #endif diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883fc..a8e0c4ff8a5 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -480,6 +480,8 @@ amproc => 'gist_point_distance' }, { amprocfamily => 'gist/point_ops', amproclefttype => 'point', amprocrighttype => 'point', amprocnum => '9', amproc => 'gist_point_fetch' }, +{ amprocfamily => 'gist/point_ops', amproclefttype => 'point', + amprocrighttype => 'point', amprocnum => '11', amproc => 'gist_point_sortsupport' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', amproc => 'gist_box_consistent' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 687509ba926..96d7efd4270 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8062,6 +8062,9 @@ proname => 'gist_poly_distance', prorettype => 'float8', proargtypes => 'internal polygon int2 oid internal', prosrc => 'gist_poly_distance' }, +{ oid => '3435', descr => 'sort support', + proname => 'gist_point_sortsupport', prorettype => 'void', + proargtypes => 'internal', prosrc => 'gist_point_sortsupport' }, # GIN array support { oid => '2743', descr => 'GIN array support', diff --git a/src/include/utils/sortsupport.h b/src/include/utils/sortsupport.h index 264aec820b1..fb262c6e8d4 100644 --- a/src/include/utils/sortsupport.h +++ b/src/include/utils/sortsupport.h @@ -272,5 +272,6 @@ extern void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup); extern void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup); extern void PrepareSortSupportFromIndexRel(Relation indexRel, int16 strategy, SortSupport ssup); +extern void PrepareSortSupportFromGistIndexRel(Relation indexRel, SortSupport ssup); #endif /* SORTSUPPORT_H */ diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h index 9e76666fe94..c69b36e209a 100644 --- a/src/include/utils/tuplesort.h +++ b/src/include/utils/tuplesort.h @@ -217,6 +217,10 @@ extern Tuplesortstate *tuplesort_begin_index_hash(Relation heapRel, uint32 max_buckets, int workMem, SortCoordinate coordinate, bool randomAccess); +extern Tuplesortstate *tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + int workMem, SortCoordinate coordinate, + bool randomAccess); extern Tuplesortstate *tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, -- 2.20.1
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-11 06:02  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey M. Borodin @ 2020-09-11 06:02 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 10 сент. 2020 г., в 17:43, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> One more patch version attached. I fixed some memory leaks, and fixed the abbreviation on 32-bit systems, and a bunch of small comment changes and such.
> 
> - Heikki
> <v18-0001-Add-support-for-building-GiST-index-by-sorting.patch>

The patch looks fine to me. On my machine GiST for points is builded 10x faster than before the patch.

Future action items:
1. Sort support for gist_btree data types
2. Better page borders with split and fillfactor
3. Consider sort build for tsvector

I'll certainly do 1 before next CF and most probably 2.
Item 1 is basically a lot of similar code for many many different types.
In Item 2 I plan to use Oleg's gevel to evaluation possibilities of MBR overlap reduction.

Item 3 seems tricky and need deeper evaluation: chances are sort build will decrease IndexScan performance in this case.

Thanks, Heikki!

Best regards, Andrey Borodin,




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-15 11:36  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  0 siblings, 3 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-15 11:36 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 11/09/2020 09:02, Andrey M. Borodin wrote:
>> 10 сент. 2020 г., в 17:43, Heikki Linnakangas <hlinnaka@iki.fi>
>> написал(а):
>> 
>> One more patch version attached. I fixed some memory leaks, and
>> fixed the abbreviation on 32-bit systems, and a bunch of small
>> comment changes and such.
>> 
>> <v18-0001-Add-support-for-building-GiST-index-by-sorting.patch>
> 
> The patch looks fine to me. On my machine GiST for points is builded
> 10x faster than before the patch.

Another patch version, fixed a few small bugs pointed out by assertion 
failures in the regression tests.

- Heikki

Attachments:

v19-0001-Add-support-for-building-GiST-index-by-sorting.patch (text/x-patch, 47.5K) download From fdf51af02513384949d4a26c2c8381e7715703b7 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas <heikki.linnakangas@iki.fi> Date: Tue, 15 Sep 2020 14:32:26 +0300 Subject: [PATCH v19 1/1] Add support for building GiST index by sorting. This adds a new optional support function to the GiST access method: sortsupport. If it is defined, the GiST index is built by sorting all data to the order defined by the sortsupport's comparator function, and packing the tuples in that order to GiST pages. This is similar to how B-tree index build works, and is much faster than inserting the tuples one by one. The resulting index is smaller too, because the pages are packed more tightly, upto 'fillfactor'. The normal build method works by splitting pages, which tends to lead to more wasted space. The quality of the resulting index depends on how good the opclass-defined sort order is. A good order preserves locality of the input data. As the first user of this facility, add 'sortsupport' function to the point_ops opclass. It sorts the points in Z-order (aka Morton Code), by interleaving the bits of the X and Y coordinates. Author: Andrey Borodin Reviewed-by: Pavel Borisov, Thomas Munro Discussion: https://www.postgresql.org/message-id/1A36620E-CAD8-4267-9067-FB31385E7C0D%40yandex-team.ru --- doc/src/sgml/gist.sgml | 70 +++ src/backend/access/gist/gistbuild.c | 510 +++++++++++++++++---- src/backend/access/gist/gistproc.c | 229 +++++++++ src/backend/access/gist/gistutil.c | 53 ++- src/backend/access/gist/gistvalidate.c | 6 +- src/backend/access/transam/xloginsert.c | 57 +++ src/backend/utils/sort/sortsupport.c | 34 ++ src/backend/utils/sort/tuplesort.c | 57 +++ src/include/access/gist.h | 3 +- src/include/access/gist_private.h | 3 + src/include/access/xloginsert.h | 2 + src/include/catalog/catversion.h | 1 + src/include/catalog/pg_amproc.dat | 2 + src/include/catalog/pg_proc.dat | 3 + src/include/utils/sortsupport.h | 1 + src/include/utils/tuplesort.h | 4 + src/test/regress/expected/create_index.out | 6 +- 17 files changed, 935 insertions(+), 106 deletions(-) diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml index f9226e7a35c..7c72a547409 100644 --- a/doc/src/sgml/gist.sgml +++ b/doc/src/sgml/gist.sgml @@ -259,6 +259,8 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); <function>compress</function> method is omitted. The optional tenth method <function>options</function> is needed if the operator class provides the user-specified parameters. + The <function>sortsupport</function> method is also optional and is used to + speed up building a <acronym>GiST</acronym> index. </para> <variablelist> @@ -1065,6 +1067,74 @@ my_compress(PG_FUNCTION_ARGS) </para> </listitem> </varlistentry> + + <varlistentry> + <term><function>sortsupport</function></term> + <listitem> + <para> + Returns a comparator function to sort data in a way that preserves + locality. It is used by <command>CREATE INDEX</command> and + <command>REINDEX</command>. The quality of the created index depends on + how well the sort order determined by the comparator function preserves + locality of the inputs. + </para> + <para> + The <function>sortsupport</function> method is optional. If it is not + provided, <command>CREATE INDEX</command> builds the index by inserting + each tuple to the tree using the <function>penalty</function> and + <function>picksplit</function> functions, which is much slower. + </para> + + <para> + The <acronym>SQL</acronym> declaration of the function must look like + this: + +<programlisting> +CREATE OR REPLACE FUNCTION my_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; +</programlisting> + + The argument is a pointer to a <structname>SortSupport</structname> + struct. At a minimum, the function must fill in its comparator field. + The comparator takes three arguments: two Datums to compare, and + a pointer to the <structname>SortSupport</structname> struct. The + Datums are the two indexed values in the format that they are stored + in the index; that is, in the format returned by the + <function>compress</function> method. The full API is defined in + <filename>src/include/utils/sortsupport.h</filename>. + </para> + + <para> + The matching code in the C module could then follow this skeleton: + +<programlisting> +PG_FUNCTION_INFO_V1(my_sortsupport); + +static int +my_fastcmp(Datum x, Datum y, SortSupport ssup) +{ + /* establish order between x and y by computing some sorting value z */ + + int z1 = ComputeSpatialCode(x); + int z2 = ComputeSpatialCode(y); + + return z1 == z2 ? 0 : z1 > z2 ? 1 : -1; +} + +Datum +my_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = my_fastcmp; + PG_RETURN_VOID(); +} +</programlisting> + </para> + </listitem> + </varlistentry> </variablelist> <para> diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index 671b5e9186f..230625cf1e2 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -3,6 +3,24 @@ * gistbuild.c * build algorithm for GiST indexes implementation. * + * There are two different strategies: + * + * 1. Sort all input tuples, pack them into GiST leaf pages in the sorted + * order, and create downlinks and internal pages as we go. This builds + * the index from the bottom up, similar to how B-tree index build + * works. + * + * 2. Start with an empty index, and insert all tuples one by one. + * + * The sorted method is used if the operator classes for all columns have + * a 'sortsupport' defined. Otherwise, we resort to the second strategy. + * + * The second strategy can optionally use buffers at different levels of + * the tree to reduce I/O, see "Buffering build algorithm" in the README + * for a more detailed explanation. It initially calls insert over and + * over, but switches to the buffered algorithm after a certain number of + * tuples (unless buffering mode is disabled). + * * * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -28,6 +46,7 @@ #include "storage/smgr.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/tuplesort.h" /* Step of index tuples for check whether to switch to buffering build mode */ #define BUFFERING_MODE_SWITCH_CHECK_STEP 256 @@ -40,8 +59,14 @@ */ #define BUFFERING_MODE_TUPLE_SIZE_STATS_TARGET 4096 +/* + * Strategy used to build the index. It can change between the + * GIST_BUFFERING_* modes on the fly, but if the Sorted method is used, + * that needs to be decided up-front and cannot be changed afterwards. + */ typedef enum { + GIST_SORTED_BUILD, /* bottom-up build by sorting */ GIST_BUFFERING_DISABLED, /* in regular build mode and aren't going to * switch */ GIST_BUFFERING_AUTO, /* in regular build mode, but will switch to @@ -51,7 +76,7 @@ typedef enum * before switching to the buffering build * mode */ GIST_BUFFERING_ACTIVE /* in buffering build mode */ -} GistBufferingMode; +} GistBuildMode; /* Working state for gistbuild and its callback */ typedef struct @@ -60,23 +85,58 @@ typedef struct Relation heaprel; GISTSTATE *giststate; - int64 indtuples; /* number of tuples indexed */ - int64 indtuplesSize; /* total size of all indexed tuples */ - Size freespace; /* amount of free space to leave on pages */ + GistBuildMode buildMode; + + int64 indtuples; /* number of tuples indexed */ + /* * Extra data structures used during a buffering build. 'gfbb' contains * information related to managing the build buffers. 'parentMap' is a * lookup table of the parent of each internal page. */ + int64 indtuplesSize; /* total size of all indexed tuples */ GISTBuildBuffers *gfbb; HTAB *parentMap; - GistBufferingMode bufferingMode; + /* + * Extra data structures used during a sorting build. + */ + Tuplesortstate *sortstate; /* state data for tuplesort.c */ + + BlockNumber pages_allocated; + BlockNumber pages_written; + + int ready_num_pages; + BlockNumber ready_blknos[XLR_MAX_BLOCK_ID]; + Page ready_pages[XLR_MAX_BLOCK_ID]; } GISTBuildState; +/* + * In sorted build, we use a stack of these structs, one for each level, + * to hold an in-memory buffer of the righmost page at the level. When the + * page fills up, it is written out and a new page is allocated. + */ +typedef struct GistSortedBuildPageState +{ + Page page; + struct GistSortedBuildPageState *parent; /* Upper level, if any */ +} GistSortedBuildPageState; + /* prototypes for private functions */ + +static void gistSortedBuildCallback(Relation index, ItemPointer tid, + Datum *values, bool *isnull, + bool tupleIsAlive, void *state); +static void gist_indexsortbuild(GISTBuildState *state); +static void gist_indexsortbuild_pagestate_add(GISTBuildState *state, + GistSortedBuildPageState *pagestate, + IndexTuple itup); +static void gist_indexsortbuild_pagestate_flush(GISTBuildState *state, + GistSortedBuildPageState *pagestate); +static void gist_indexsortbuild_flush_ready_pages(GISTBuildState *state); + static void gistInitBuffering(GISTBuildState *buildstate); static int calculatePagesPerBuffer(GISTBuildState *buildstate, int levelStep); static void gistBuildCallback(Relation index, @@ -107,10 +167,9 @@ static void gistMemorizeParent(GISTBuildState *buildstate, BlockNumber child, static void gistMemorizeAllDownlinks(GISTBuildState *buildstate, Buffer parent); static BlockNumber gistGetParent(GISTBuildState *buildstate, BlockNumber child); + /* - * Main entry point to GiST index build. Initially calls insert over and over, - * but switches to more efficient buffering build algorithm after a certain - * number of tuples (unless buffering mode is disabled). + * Main entry point to GiST index build. */ IndexBuildResult * gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) @@ -118,124 +177,407 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) IndexBuildResult *result; double reltuples; GISTBuildState buildstate; - Buffer buffer; - Page page; MemoryContext oldcxt = CurrentMemoryContext; int fillfactor; + Oid SortSupportFnOids[INDEX_MAX_KEYS]; + bool hasallsortsupports; + int keyscount = IndexRelationGetNumberOfKeyAttributes(index); + GiSTOptions *options = NULL; + + /* + * We expect to be called exactly once for any index relation. If that's + * not the case, big trouble's what we have. + */ + if (RelationGetNumberOfBlocks(index) != 0) + elog(ERROR, "index \"%s\" already contains data", + RelationGetRelationName(index)); + + if (index->rd_options) + options = (GiSTOptions *) index->rd_options; buildstate.indexrel = index; buildstate.heaprel = heap; + buildstate.sortstate = NULL; + buildstate.giststate = initGISTstate(index); - if (index->rd_options) + /* + * Create a temporary memory context that is reset once for each tuple + * processed. (Note: we don't bother to make this a child of the + * giststate's scanCxt, so we have to delete it separately at the end.) + */ + buildstate.giststate->tempCxt = createTempGistContext(); + + /* + * Choose build strategy. If all keys support sorting, do that. Otherwise + * the default strategy is switch to buffering mode when the index grows + * too large to fit in cache. + */ + hasallsortsupports = true; + for (int i = 0; i < keyscount; i++) { - /* Get buffering mode from the options string */ - GiSTOptions *options = (GiSTOptions *) index->rd_options; + SortSupportFnOids[i] = index_getprocid(index, i + 1, + GIST_SORTSUPPORT_PROC); + if (!OidIsValid(SortSupportFnOids[i])) + { + hasallsortsupports = false; + break; + } + } + if (hasallsortsupports) + { + buildstate.buildMode = GIST_SORTED_BUILD; + } + else if (options) + { if (options->buffering_mode == GIST_OPTION_BUFFERING_ON) - buildstate.bufferingMode = GIST_BUFFERING_STATS; + buildstate.buildMode = GIST_BUFFERING_STATS; else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF) - buildstate.bufferingMode = GIST_BUFFERING_DISABLED; + buildstate.buildMode = GIST_BUFFERING_DISABLED; else - buildstate.bufferingMode = GIST_BUFFERING_AUTO; - - fillfactor = options->fillfactor; + buildstate.buildMode = GIST_BUFFERING_AUTO; } else { - /* - * By default, switch to buffering mode when the index grows too large - * to fit in cache. - */ - buildstate.bufferingMode = GIST_BUFFERING_AUTO; - fillfactor = GIST_DEFAULT_FILLFACTOR; + buildstate.buildMode = GIST_BUFFERING_AUTO; } - /* Calculate target amount of free space to leave on pages */ + + /* + * Calculate target amount of free space to leave on pages. + */ + fillfactor = options ? options->fillfactor : GIST_DEFAULT_FILLFACTOR; buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100; /* - * We expect to be called exactly once for any index relation. If that's - * not the case, big trouble's what we have. + * Build the index using the chosen strategy. */ - if (RelationGetNumberOfBlocks(index) != 0) - elog(ERROR, "index \"%s\" already contains data", - RelationGetRelationName(index)); + buildstate.indtuples = 0; + buildstate.indtuplesSize = 0; - /* no locking is needed */ - buildstate.giststate = initGISTstate(index); + if (buildstate.buildMode == GIST_SORTED_BUILD) + { + /* + * Sort all data, build the index from bottom up. + */ + buildstate.sortstate = tuplesort_begin_index_gist(heap, + index, + maintenance_work_mem, + NULL, + false); + + /* Scan the table, adding all tuples to the tuplesort */ + reltuples = table_index_build_scan(heap, index, indexInfo, true, true, + gistSortedBuildCallback, + (void *) &buildstate, NULL); + + /* + * Perform the sort and build index pages. + */ + tuplesort_performsort(buildstate.sortstate); + + gist_indexsortbuild(&buildstate); + + tuplesort_end(buildstate.sortstate); + } + else + { + /* + * Initialize an empty index and insert all tuples, possibly using + * buffers on intermediate levels. + */ + Buffer buffer; + Page page; + + /* initialize the root page */ + buffer = gistNewBuffer(index); + Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO); + page = BufferGetPage(buffer); + + START_CRIT_SECTION(); + + GISTInitBuffer(buffer, F_LEAF); + + MarkBufferDirty(buffer); + PageSetLSN(page, GistBuildLSN); + + UnlockReleaseBuffer(buffer); + + END_CRIT_SECTION(); + + /* Scan the table, inserting all the tuples to the index. */ + reltuples = table_index_build_scan(heap, index, indexInfo, true, true, + gistBuildCallback, + (void *) &buildstate, NULL); + + /* + * If buffering was used, flush out all the tuples that are still in + * the buffers. + */ + if (buildstate.buildMode == GIST_BUFFERING_ACTIVE) + { + elog(DEBUG1, "all tuples processed, emptying buffers"); + gistEmptyAllBuffers(&buildstate); + gistFreeBuildBuffers(buildstate.gfbb); + } + + /* + * We didn't write WAL records as we built the index, so if + * WAL-logging is required, write all pages to the WAL now. + */ + if (RelationNeedsWAL(index)) + { + log_newpage_range(index, MAIN_FORKNUM, + 0, RelationGetNumberOfBlocks(index), + true); + } + } + + /* okay, all heap tuples are indexed */ + MemoryContextSwitchTo(oldcxt); + MemoryContextDelete(buildstate.giststate->tempCxt); + + freeGISTstate(buildstate.giststate); /* - * Create a temporary memory context that is reset once for each tuple - * processed. (Note: we don't bother to make this a child of the - * giststate's scanCxt, so we have to delete it separately at the end.) + * Return statistics */ - buildstate.giststate->tempCxt = createTempGistContext(); + result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); - /* initialize the root page */ - buffer = gistNewBuffer(index); - Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO); - page = BufferGetPage(buffer); + result->heap_tuples = reltuples; + result->index_tuples = (double) buildstate.indtuples; - START_CRIT_SECTION(); + return result; +} - GISTInitBuffer(buffer, F_LEAF); +/*------------------------------------------------------------------------- + * Routines for sorted build + *------------------------------------------------------------------------- + */ - MarkBufferDirty(buffer); - PageSetLSN(page, GistBuildLSN); +/* + * Per-tuple callback for table_index_build_scan. + */ +static void +gistSortedBuildCallback(Relation index, + ItemPointer tid, + Datum *values, + bool *isnull, + bool tupleIsAlive, + void *state) +{ + GISTBuildState *buildstate = (GISTBuildState *) state; + MemoryContext oldCtx; + Datum compressed_values[INDEX_MAX_KEYS]; - UnlockReleaseBuffer(buffer); + oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); - END_CRIT_SECTION(); + /* Form an index tuple and point it at the heap tuple */ + gistCompressValues(buildstate->giststate, index, + values, isnull, + true, compressed_values); - /* build the index */ - buildstate.indtuples = 0; - buildstate.indtuplesSize = 0; + tuplesort_putindextuplevalues(buildstate->sortstate, + buildstate->indexrel, + tid, + compressed_values, isnull); + + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(buildstate->giststate->tempCxt); + + /* Update tuple count. */ + buildstate->indtuples += 1; +} + +/* + * Build GiST index from bottom up from pre-sorted tuples. + */ +static void +gist_indexsortbuild(GISTBuildState *state) +{ + IndexTuple itup; + GistSortedBuildPageState *leafstate; + GistSortedBuildPageState *pagestate; + Page page; + + state->pages_allocated = 0; + state->pages_written = 0; + state->ready_num_pages = 0; /* - * Do the heap scan. + * Write an empty page as a placeholder for the root page. It will be + * replaced with the real root page at the end. */ - reltuples = table_index_build_scan(heap, index, indexInfo, true, true, - gistBuildCallback, - (void *) &buildstate, NULL); + page = palloc0(BLCKSZ); + smgrextend(state->indexrel->rd_smgr, MAIN_FORKNUM, GIST_ROOT_BLKNO, + page, true); + state->pages_allocated++; + state->pages_written++; + + /* Allocate a temporary buffer for the first leaf page. */ + leafstate = palloc(sizeof(GistSortedBuildPageState)); + leafstate->page = page; + leafstate->parent = NULL; + gistinitpage(page, F_LEAF); /* - * If buffering was used, flush out all the tuples that are still in the - * buffers. + * Fill index pages with tuples in the sorted order. */ - if (buildstate.bufferingMode == GIST_BUFFERING_ACTIVE) + while ((itup = tuplesort_getindextuple(state->sortstate, true)) != NULL) { - elog(DEBUG1, "all tuples processed, emptying buffers"); - gistEmptyAllBuffers(&buildstate); - gistFreeBuildBuffers(buildstate.gfbb); + gist_indexsortbuild_pagestate_add(state, leafstate, itup); + MemoryContextReset(state->giststate->tempCxt); } - /* okay, all heap tuples are indexed */ - MemoryContextSwitchTo(oldcxt); - MemoryContextDelete(buildstate.giststate->tempCxt); - - freeGISTstate(buildstate.giststate); - /* - * We didn't write WAL records as we built the index, so if WAL-logging is - * required, write all pages to the WAL now. + * Write out the partially full non-root pages. + * + * Keep in mind that flush can build a new root. */ - if (RelationNeedsWAL(index)) + pagestate = leafstate; + while (pagestate->parent != NULL) { - log_newpage_range(index, MAIN_FORKNUM, - 0, RelationGetNumberOfBlocks(index), - true); + GistSortedBuildPageState *parent; + + gist_indexsortbuild_pagestate_flush(state, pagestate); + parent = pagestate->parent; + pfree(pagestate->page); + pfree(pagestate); + pagestate = parent; } + gist_indexsortbuild_flush_ready_pages(state); + + /* Write out the root */ + PageSetLSN(pagestate->page, GistBuildLSN); + smgrwrite(state->indexrel->rd_smgr, MAIN_FORKNUM, GIST_ROOT_BLKNO, + pagestate->page, true); + if (RelationNeedsWAL(state->indexrel)) + log_newpage(&state->indexrel->rd_node, MAIN_FORKNUM, GIST_ROOT_BLKNO, + pagestate->page, true); + + pfree(pagestate->page); + pfree(pagestate); +} + +/* + * Add tuple to a page. If the pages is full, write it out and re-initialize + * a new page first. + */ +static void +gist_indexsortbuild_pagestate_add(GISTBuildState *state, + GistSortedBuildPageState *pagestate, + IndexTuple itup) +{ + Size sizeNeeded; + + /* Does the tuple fit? If not, flush */ + sizeNeeded = IndexTupleSize(itup) + sizeof(ItemIdData) + state->freespace; + if (PageGetFreeSpace(pagestate->page) < sizeNeeded) + gist_indexsortbuild_pagestate_flush(state, pagestate); + + gistfillbuffer(pagestate->page, &itup, 1, InvalidOffsetNumber); +} + +static void +gist_indexsortbuild_pagestate_flush(GISTBuildState *state, + GistSortedBuildPageState *pagestate) +{ + GistSortedBuildPageState *parent; + IndexTuple *itvec; + IndexTuple union_tuple; + int vect_len; + bool isleaf; + BlockNumber blkno; + MemoryContext oldCtx; + + /* check once per page */ + CHECK_FOR_INTERRUPTS(); + + if (state->ready_num_pages == XLR_MAX_BLOCK_ID) + gist_indexsortbuild_flush_ready_pages(state); + /* - * Return statistics + * The page is now complete. Assign a block number to it, and add it to + * the list of finished pages. (We don't write it out immediately, because + * we want to WAL-log the pages in batches.) */ - result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); + blkno = state->pages_allocated++; + state->ready_blknos[state->ready_num_pages] = blkno; + state->ready_pages[state->ready_num_pages] = pagestate->page; + state->ready_num_pages++; - result->heap_tuples = reltuples; - result->index_tuples = (double) buildstate.indtuples; + isleaf = GistPageIsLeaf(pagestate->page); - return result; + /* + * Form a downlink tuple to represent all the tuples on the page. + */ + oldCtx = MemoryContextSwitchTo(state->giststate->tempCxt); + itvec = gistextractpage(pagestate->page, &vect_len); + union_tuple = gistunion(state->indexrel, itvec, vect_len, + state->giststate); + ItemPointerSetBlockNumber(&(union_tuple->t_tid), blkno); + MemoryContextSwitchTo(oldCtx); + + /* + * Insert the downlink to the parent page. If this was the root, create a + * new page as the parent, which becomes the new root. + */ + parent = pagestate->parent; + if (parent == NULL) + { + parent = palloc(sizeof(GistSortedBuildPageState)); + parent->page = (Page) palloc(BLCKSZ); + parent->parent = NULL; + gistinitpage(parent->page, 0); + + pagestate->parent = parent; + } + gist_indexsortbuild_pagestate_add(state, parent, union_tuple); + + /* Re-initialize the page buffer for next page on this level. */ + pagestate->page = palloc(BLCKSZ); + gistinitpage(pagestate->page, isleaf ? F_LEAF : 0); +} + +static void +gist_indexsortbuild_flush_ready_pages(GISTBuildState *state) +{ + if (state->ready_num_pages == 0) + return; + + for (int i = 0; i < state->ready_num_pages; i++) + { + Page page = state->ready_pages[i]; + + /* Currently, the blocks must be buffered in order. */ + if (state->ready_blknos[i] != state->pages_written) + elog(ERROR, "unexpected block number to flush GiST sorting build"); + + PageSetLSN(page, GistBuildLSN); + + smgrextend(state->indexrel->rd_smgr, + MAIN_FORKNUM, + state->pages_written++, + page, + true); + } + + if (RelationNeedsWAL(state->indexrel)) + log_newpages(&state->indexrel->rd_node, MAIN_FORKNUM, state->ready_num_pages, + state->ready_blknos, state->ready_pages, true); + + for (int i = 0; i < state->ready_num_pages; i++) + pfree(state->ready_pages[i]); + + state->ready_num_pages = 0; } + +/*------------------------------------------------------------------------- + * Routines for non-sorted build + *------------------------------------------------------------------------- + */ + /* * Attempt to switch to buffering mode. * @@ -375,7 +717,7 @@ gistInitBuffering(GISTBuildState *buildstate) if (levelStep <= 0) { elog(DEBUG1, "failed to switch to buffered GiST build"); - buildstate->bufferingMode = GIST_BUFFERING_DISABLED; + buildstate->buildMode = GIST_BUFFERING_DISABLED; return; } @@ -392,7 +734,7 @@ gistInitBuffering(GISTBuildState *buildstate) gistInitParentMap(buildstate); - buildstate->bufferingMode = GIST_BUFFERING_ACTIVE; + buildstate->buildMode = GIST_BUFFERING_ACTIVE; elog(DEBUG1, "switched to buffered GiST build; level step = %d, pagesPerBuffer = %d", levelStep, pagesPerBuffer); @@ -453,10 +795,12 @@ gistBuildCallback(Relation index, oldCtx = MemoryContextSwitchTo(buildstate->giststate->tempCxt); /* form an index tuple and point it at the heap tuple */ - itup = gistFormTuple(buildstate->giststate, index, values, isnull, true); + itup = gistFormTuple(buildstate->giststate, index, + values, isnull, + true); itup->t_tid = *tid; - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE) + if (buildstate->buildMode == GIST_BUFFERING_ACTIVE) { /* We have buffers, so use them. */ gistBufferingBuildInsert(buildstate, itup); @@ -478,7 +822,7 @@ gistBuildCallback(Relation index, MemoryContextSwitchTo(oldCtx); MemoryContextReset(buildstate->giststate->tempCxt); - if (buildstate->bufferingMode == GIST_BUFFERING_ACTIVE && + if (buildstate->buildMode == GIST_BUFFERING_ACTIVE && buildstate->indtuples % BUFFERING_MODE_TUPLE_SIZE_STATS_TARGET == 0) { /* Adjust the target buffer size now */ @@ -493,10 +837,10 @@ gistBuildCallback(Relation index, * To avoid excessive calls to smgrnblocks(), only check this every * BUFFERING_MODE_SWITCH_CHECK_STEP index tuples */ - if ((buildstate->bufferingMode == GIST_BUFFERING_AUTO && + if ((buildstate->buildMode == GIST_BUFFERING_AUTO && buildstate->indtuples % BUFFERING_MODE_SWITCH_CHECK_STEP == 0 && effective_cache_size < smgrnblocks(index->rd_smgr, MAIN_FORKNUM)) || - (buildstate->bufferingMode == GIST_BUFFERING_STATS && + (buildstate->buildMode == GIST_BUFFERING_STATS && buildstate->indtuples >= BUFFERING_MODE_TUPLE_SIZE_STATS_TARGET)) { /* diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 9ace64c3c4a..27d9c0f77c3 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -24,6 +24,7 @@ #include "utils/builtins.h" #include "utils/float.h" #include "utils/geo_decls.h" +#include "utils/sortsupport.h" static bool gist_box_leaf_consistent(BOX *key, BOX *query, @@ -31,6 +32,15 @@ static bool gist_box_leaf_consistent(BOX *key, BOX *query, static bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy); +static uint64 point_zorder_internal(float4 x, float4 y); +static uint64 part_bits32_by2(uint32 x); +static uint32 ieee_float32_to_uint32(float f); +static int gist_bbox_zorder_cmp(Datum a, Datum b, SortSupport ssup); +static Datum gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup); +static int gist_bbox_zorder_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup); +static bool gist_bbox_zorder_abbrev_abort(int memtupcount, SortSupport ssup); + + /* Minimum accepted ratio of split */ #define LIMIT_RATIO 0.3 @@ -1540,3 +1550,222 @@ gist_poly_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } + +/* + * Z-order routines for fast index build + */ + +/* + * Compute Z-value of a point + * + * Z-order (also known as Morton Code) maps a two-dimensional point to a + * single integer, in a way that preserves locality. Points that are close in + * the two-dimensional space are mapped to integer that are not far from each + * other. We do that by interleaving the bits in the X and Y components. + * + * Morton Code is normally defined only for integers, but the X and Y values + * of a point are floating point. We expect floats to be in IEEE format. + */ +static uint64 +point_zorder_internal(float4 x, float4 y) +{ + uint32 ix = ieee_float32_to_uint32(x); + uint32 iy = ieee_float32_to_uint32(y); + + /* Interleave the bits */ + return part_bits32_by2(ix) | (part_bits32_by2(iy) << 1); +} + +/* Interleave 32 bits with zeroes */ +static uint64 +part_bits32_by2(uint32 x) +{ + uint64 n = x; + + n = (n | (n << 16)) & UINT64CONST(0x0000FFFF0000FFFF); + n = (n | (n << 8)) & UINT64CONST(0x00FF00FF00FF00FF); + n = (n | (n << 4)) & UINT64CONST(0x0F0F0F0F0F0F0F0F); + n = (n | (n << 2)) & UINT64CONST(0x3333333333333333); + n = (n | (n << 1)) & UINT64CONST(0x5555555555555555); + + return n; +} + +/* + * Convert a 32-bit IEEE float to uint32 in a way that preserves the ordering + */ +static uint32 +ieee_float32_to_uint32(float f) +{ + /*---- + * + * IEEE 754 floating point format + * ------------------------------ + * + * IEEE 754 floating point numbers have this format: + * + * exponent (8 bits) + * | + * s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm + * | | + * sign mantissa (23 bits) + * + * Infinity has all bits in the exponent set and the mantissa is all + * zeros. Negative infinity is the same but with the sign bit set. + * + * NaNs are represented with all bits in the exponent set, and the least + * significant bit in the mantissa also set. The rest of the mantissa bits + * can be used to distinguish different kinds of NaNs. + * + * The IEEE format has the nice property that when you take the bit + * representation and interpret it as an integer, the order is preserved, + * except for the sign. That holds for the +-Infinity values too. + * + * Mapping to uint32 + * ----------------- + * + * In order to have a smooth transition from negative to positive numbers, + * we map floats to unsigned integers like this: + * + * x < 0 to range 0-7FFFFFFF + * x = 0 to value 8000000 (both positive and negative zero) + * x > 0 to range 8000001-FFFFFFFF + * + * We don't care to distinguish different kind of NaNs, so they are all + * mapped to the same arbitrary value, FFFFFFFF. Because of the IEEE bit + * representation of NaNs, there aren't any non-NaN values that would be + * mapped to FFFFFFFF. In fact, there is a range of unused values on both + * ends of the uint32 space. + */ + if (isnan(f)) + return 0xFFFFFFFF; + else + { + union + { + float f; + uint32 i; + } u; + + u.f = f; + + /* Check the sign bit */ + if ((u.i & 0x80000000) != 0) + { + /* + * Map the negative value to range 0-7FFFFFFF. This flips the sign + * bit to 0 in the same instruction. + */ + Assert(f <= 0); /* can be -0 */ + u.i ^= 0xFFFFFFFF; + } + else + { + /* Map the positive value (or 0) to range 80000000-FFFFFFFF */ + u.i |= 0x80000000; + } + + return u.i; + } +} + +/* + * Compare the Z-order of points + */ +static int +gist_bbox_zorder_cmp(Datum a, Datum b, SortSupport ssup) +{ + Point *p1 = &(DatumGetBoxP(a)->low); + Point *p2 = &(DatumGetBoxP(b)->low); + uint64 z1; + uint64 z2; + + /* + * Do a quick check for equality first. It's not clear if this is worth it + * in general, but certainly is when used as tie-breaker with abbreviated + * keys, + */ + if (p1->x == p2->x && p1->y == p2->y) + return 0; + + z1 = point_zorder_internal(p1->x, p1->y); + z2 = point_zorder_internal(p2->x, p2->y); + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * Abbreviated version of Z-order comparison + * + * The abbreviated format is a Z-order value computed from the two 32-bit + * floats. If SIZEOF_DATUM == 8, the 64-bit Z-order value fits fully in the + * abbreviated Datum, otherwise use its most significant bits. + */ +static Datum +gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup) +{ + Point *p = &(DatumGetBoxP(original)->low); + uint64 z; + + z = point_zorder_internal(p->x, p->y); + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gist_bbox_zorder_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + /* + * Compare the pre-computed Z-orders as unsigned integers. Datum is a + * typedef for 'uintptr_t', so no casting is required. + */ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + * + * On 64-bit systems, the abbreviation is not lossy so it is always + * worthwhile. (Perhaps it's not on 32-bit systems, but we don't bother + * with logic to decide.) + */ +static bool +gist_bbox_zorder_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gist_point_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gist_bbox_zorder_cmp_abbrev; + ssup->abbrev_converter = gist_bbox_zorder_abbrev_convert; + ssup->abbrev_abort = gist_bbox_zorder_abbrev_abort; + ssup->abbrev_full_comparator = gist_bbox_zorder_cmp; + } + else + { + ssup->comparator = gist_bbox_zorder_cmp; + } + PG_RETURN_VOID(); +} diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 0516059e3dd..615b5ade233 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -572,12 +572,31 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, - Datum attdata[], bool isnull[], bool isleaf) + Datum *attdata, bool *isnull, bool isleaf) { Datum compatt[INDEX_MAX_KEYS]; - int i; IndexTuple res; + gistCompressValues(giststate, r, attdata, isnull, isleaf, compatt); + + res = index_form_tuple(isleaf ? giststate->leafTupdesc : + giststate->nonLeafTupdesc, + compatt, isnull); + + /* + * The offset number on tuples on internal pages is unused. For historical + * reasons, it is set to 0xffff. + */ + ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff); + return res; +} + +void +gistCompressValues(GISTSTATE *giststate, Relation r, + Datum *attdata, bool *isnull, bool isleaf, Datum *compatt) +{ + int i; + /* * Call the compress method on each attribute. */ @@ -617,17 +636,6 @@ gistFormTuple(GISTSTATE *giststate, Relation r, compatt[i] = attdata[i]; } } - - res = index_form_tuple(isleaf ? giststate->leafTupdesc : - giststate->nonLeafTupdesc, - compatt, isnull); - - /* - * The offset number on tuples on internal pages is unused. For historical - * reasons, it is set to 0xffff. - */ - ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff); - return res; } /* @@ -745,14 +753,11 @@ gistpenalty(GISTSTATE *giststate, int attno, * Initialize a new index page */ void -GISTInitBuffer(Buffer b, uint32 f) +gistinitpage(Page page, uint32 f) { GISTPageOpaque opaque; - Page page; - Size pageSize; + Size pageSize = BLCKSZ; - pageSize = BufferGetPageSize(b); - page = BufferGetPage(b); PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); opaque = GistPageGetOpaque(page); @@ -763,6 +768,18 @@ GISTInitBuffer(Buffer b, uint32 f) opaque->gist_page_id = GIST_PAGE_ID; } +/* + * Initialize a new index buffer + */ +void +GISTInitBuffer(Buffer b, uint32 f) +{ + Page page; + + page = BufferGetPage(b); + gistinitpage(page, f); +} + /* * Verify that a freshly-read page looks sane. */ diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index 2b9ab693be1..8a14620fab2 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -143,6 +143,10 @@ gistvalidate(Oid opclassoid) case GIST_OPTIONS_PROC: ok = check_amoptsproc_signature(procform->amproc); break; + case GIST_SORTSUPPORT_PROC: + ok = check_amproc_signature(procform->amproc, VOIDOID, true, + 1, 1, INTERNALOID); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -263,7 +267,7 @@ gistvalidate(Oid opclassoid) continue; /* got it */ if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC || i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC || - i == GIST_OPTIONS_PROC) + i == GIST_OPTIONS_PROC || i == GIST_SORTSUPPORT_PROC) continue; /* optional methods */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c index c526bb19281..1f0e4e01e69 100644 --- a/src/backend/access/transam/xloginsert.c +++ b/src/backend/access/transam/xloginsert.c @@ -1019,6 +1019,63 @@ log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno, return recptr; } +/* + * Like log_newpage(), but allows logging multiple pages in one operation. + * It is more efficient than calling log_newpage() for each page separately, + * because we can write multiple pages in a single WAL record. + */ +void +log_newpages(RelFileNode *rnode, ForkNumber forkNum, int num_pages, + BlockNumber *blknos, Page *pages, bool page_std) +{ + int flags; + XLogRecPtr recptr; + int i; + int j; + + flags = REGBUF_FORCE_IMAGE; + if (page_std) + flags |= REGBUF_STANDARD; + + /* + * Iterate over all the pages. They are collected into batches of + * XLR_MAX_BLOCK_ID pages, and a single WAL-record is written for each + * batch. + */ + XLogEnsureRecordSpace(XLR_MAX_BLOCK_ID - 1, 0); + + i = 0; + while (i < num_pages) + { + int batch_start = i; + int nbatch; + + XLogBeginInsert(); + + nbatch = 0; + while (nbatch < XLR_MAX_BLOCK_ID && i < num_pages) + { + XLogRegisterBlock(nbatch, rnode, forkNum, blknos[i], pages[i], flags); + i++; + nbatch++; + } + + recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI); + + for (j = batch_start; j < i; j++) + { + /* + * The page may be uninitialized. If so, we can't set the LSN because that + * would corrupt the page. + */ + if (!PageIsNew(pages[j])) + { + PageSetLSN(pages[j], recptr); + } + } + } +} + /* * Write a WAL record containing a full image of a page. * diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c index fcfe6e831a1..c436fbb4ce1 100644 --- a/src/backend/utils/sort/sortsupport.c +++ b/src/backend/utils/sort/sortsupport.c @@ -15,6 +15,7 @@ #include "postgres.h" +#include "access/gist.h" #include "access/nbtree.h" #include "catalog/pg_am.h" #include "fmgr.h" @@ -175,3 +176,36 @@ PrepareSortSupportFromIndexRel(Relation indexRel, int16 strategy, FinishSortSupportFunction(opfamily, opcintype, ssup); } + +/* + * Fill in SortSupport given a GiST index relation + * + * Caller must previously have zeroed the SortSupportData structure and then + * filled in ssup_cxt, ssup_attno, ssup_collation, and ssup_nulls_first. This + * will fill in ssup_reverse (always false for GiST index build), as well as + * the comparator function pointer. + */ +void +PrepareSortSupportFromGistIndexRel(Relation indexRel, SortSupport ssup) +{ + Oid opfamily = indexRel->rd_opfamily[ssup->ssup_attno - 1]; + Oid opcintype = indexRel->rd_opcintype[ssup->ssup_attno - 1]; + Oid sortSupportFunction; + + Assert(ssup->comparator == NULL); + + if (indexRel->rd_rel->relam != GIST_AM_OID) + elog(ERROR, "unexpected non-gist AM: %u", indexRel->rd_rel->relam); + ssup->ssup_reverse = false; + + /* + * Look up the sort support function. This is simpler than for B-tree + * indexes because we don't support the old-style btree comparators. + */ + sortSupportFunction = get_opfamily_proc(opfamily, opcintype, opcintype, + GIST_SORTSUPPORT_PROC); + if (!OidIsValid(sortSupportFunction)) + elog(ERROR, "missing support function %d(%u,%u) in opfamily %u", + GIST_SORTSUPPORT_PROC, opcintype, opcintype, opfamily); + OidFunctionCall1(sortSupportFunction, PointerGetDatum(ssup)); +} diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index cbda911f465..d0cc04a878a 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -1167,6 +1167,63 @@ tuplesort_begin_index_hash(Relation heapRel, return state; } +Tuplesortstate * +tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + int workMem, + SortCoordinate coordinate, + bool randomAccess) +{ + Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, + randomAccess); + MemoryContext oldcontext; + int i; + + oldcontext = MemoryContextSwitchTo(state->sortcontext); + +#ifdef TRACE_SORT + if (trace_sort) + elog(LOG, + "begin index sort: workMem = %d, randomAccess = %c", + workMem, randomAccess ? 't' : 'f'); +#endif + + state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); + + state->comparetup = comparetup_index_btree; + state->copytup = copytup_index; + state->writetup = writetup_index; + state->readtup = readtup_index; + + state->heapRel = heapRel; + state->indexRel = indexRel; + + /* Prepare SortSupport data for each column */ + state->sortKeys = (SortSupport) palloc0(state->nKeys * + sizeof(SortSupportData)); + + for (i = 0; i < state->nKeys; i++) + { + SortSupport sortKey = state->sortKeys + i; + + sortKey->ssup_cxt = CurrentMemoryContext; + sortKey->ssup_collation = indexRel->rd_indcollation[i]; + sortKey->ssup_nulls_first = false; + sortKey->ssup_attno = i + 1; + /* Convey if abbreviation optimization is applicable in principle */ + sortKey->abbreviate = (i == 0); + + AssertState(sortKey->ssup_attno != 0); + + /* Look for a sort support function */ + PrepareSortSupportFromGistIndexRel(indexRel, sortKey); + } + + MemoryContextSwitchTo(oldcontext); + + return state; +} + Tuplesortstate * tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 4994351697c..4f6dae9a76b 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -37,7 +37,8 @@ #define GIST_DISTANCE_PROC 8 #define GIST_FETCH_PROC 9 #define GIST_OPTIONS_PROC 10 -#define GISTNProcs 10 +#define GIST_SORTSUPPORT_PROC 11 +#define GISTNProcs 11 /* * Page opaque data in a GiST index page. diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 02e985549f6..b68c01a5f24 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -501,12 +501,15 @@ extern IndexTuple gistgetadjusted(Relation r, GISTSTATE *giststate); extern IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf); +extern void gistCompressValues(GISTSTATE *giststate, Relation r, + Datum *attdata, bool *isnull, bool isleaf, Datum *compatt); extern OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate); extern void GISTInitBuffer(Buffer b, uint32 f); +extern void gistinitpage(Page page, uint32 f); extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull); diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h index 63df25ae90f..4ba2c56be60 100644 --- a/src/include/access/xloginsert.h +++ b/src/include/access/xloginsert.h @@ -54,6 +54,8 @@ extern bool XLogCheckBufferNeedsBackup(Buffer buffer); extern XLogRecPtr log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blk, char *page, bool page_std); +extern void log_newpages(RelFileNode *rnode, ForkNumber forkNum, int num_pages, + BlockNumber *blknos, char **pages, bool page_std); extern XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std); extern void log_newpage_range(Relation rel, ForkNumber forkNum, BlockNumber startblk, BlockNumber endblk, bool page_std); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 0bbe0a122af..06ddb1f16b4 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,7 @@ */ /* yyyymmddN */ +/* FIXME: bump this before pushing! */ #define CATALOG_VERSION_NO 202009031 #endif diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 37b580883fc..a8e0c4ff8a5 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -480,6 +480,8 @@ amproc => 'gist_point_distance' }, { amprocfamily => 'gist/point_ops', amproclefttype => 'point', amprocrighttype => 'point', amprocnum => '9', amproc => 'gist_point_fetch' }, +{ amprocfamily => 'gist/point_ops', amproclefttype => 'point', + amprocrighttype => 'point', amprocnum => '11', amproc => 'gist_point_sortsupport' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', amprocrighttype => 'box', amprocnum => '1', amproc => 'gist_box_consistent' }, { amprocfamily => 'gist/box_ops', amproclefttype => 'box', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 687509ba926..96d7efd4270 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8062,6 +8062,9 @@ proname => 'gist_poly_distance', prorettype => 'float8', proargtypes => 'internal polygon int2 oid internal', prosrc => 'gist_poly_distance' }, +{ oid => '3435', descr => 'sort support', + proname => 'gist_point_sortsupport', prorettype => 'void', + proargtypes => 'internal', prosrc => 'gist_point_sortsupport' }, # GIN array support { oid => '2743', descr => 'GIN array support', diff --git a/src/include/utils/sortsupport.h b/src/include/utils/sortsupport.h index 264aec820b1..fb262c6e8d4 100644 --- a/src/include/utils/sortsupport.h +++ b/src/include/utils/sortsupport.h @@ -272,5 +272,6 @@ extern void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup); extern void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup); extern void PrepareSortSupportFromIndexRel(Relation indexRel, int16 strategy, SortSupport ssup); +extern void PrepareSortSupportFromGistIndexRel(Relation indexRel, SortSupport ssup); #endif /* SORTSUPPORT_H */ diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h index 9e76666fe94..c69b36e209a 100644 --- a/src/include/utils/tuplesort.h +++ b/src/include/utils/tuplesort.h @@ -217,6 +217,10 @@ extern Tuplesortstate *tuplesort_begin_index_hash(Relation heapRel, uint32 max_buckets, int workMem, SortCoordinate coordinate, bool randomAccess); +extern Tuplesortstate *tuplesort_begin_index_gist(Relation heapRel, + Relation indexRel, + int workMem, SortCoordinate coordinate, + bool randomAccess); extern Tuplesortstate *tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index 64c0c668593..6ace7662ee1 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -523,8 +523,8 @@ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; f1 ------------------- - (0,0) (1e-300,-1e-300) + (0,0) (-3,4) (-10,0) (10,10) @@ -561,8 +561,8 @@ SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1'; f1 ------------------- - (0,0) (1e-300,-1e-300) + (0,0) (-3,4) (-10,0) (10,10) @@ -584,8 +584,8 @@ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0 SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; f1 ------------------ - (0,0) (1e-300,-1e-300) + (0,0) (-3,4) (-10,0) (10,10) -- 2.20.1
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-15 16:46  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  2 siblings, 1 reply; 111+ messages in thread

From: Andrey M. Borodin @ 2020-09-15 16:46 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 15 сент. 2020 г., в 16:36, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> Another patch version, fixed a few small bugs pointed out by assertion failures in the regression tests.
> 
> - Heikki
> <v19-0001-Add-support-for-building-GiST-index-by-sorting.patch>

These changes in create_index.out do not seem correct to me

 SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
         f1         
 -------------------
- (0,0)
  (1e-300,-1e-300)
+ (0,0)

I did not figure out the root cause yet. We do not touch anything related to distance computation..

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-15 17:07  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-15 17:07 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 15/09/2020 19:46, Andrey M. Borodin wrote:
> 
> 
>> 15 сент. 2020 г., в 16:36, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
>>
>> Another patch version, fixed a few small bugs pointed out by assertion failures in the regression tests.
>>
>> - Heikki
>> <v19-0001-Add-support-for-building-GiST-index-by-sorting.patch>
> 
> These changes in create_index.out do not seem correct to me
> 
>   SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
>           f1
>   -------------------
> - (0,0)
>    (1e-300,-1e-300)
> + (0,0)
> 
> I did not figure out the root cause yet. We do not touch anything related to distance computation..

Ah yeah, that's subtle. Those rows are considered to be equally distant 
from (0, 1), given the precision of the <-> operator:

regression=#  SELECT f1, f1 <-> '0,1' FROM point_tbl ORDER BY f1 <-> '0,1';
         f1         |     ?column?
-------------------+------------------
  (0,0)             |                1
  (1e-300,-1e-300)  |                1
  (-3,4)            | 4.24264068711929
  (-10,0)           | 10.0498756211209
  (10,10)           | 13.4536240470737
  (-5,-12)          | 13.9283882771841
  (5.1,34.5)        |  33.885985303662
  (1e+300,Infinity) |         Infinity
  (NaN,NaN)         |              NaN
                    |
(10 rows)

It is arbitrary which one you get first.

It's not very nice to have a not-well defined order of rows in the 
expected output, as it could change in the future if we change the index 
build algorithm again. But we have plenty of cases that depend on the 
physical row order, and it's not like this changes very often, so I 
think it's ok to just memorize the new order in the expected output.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-16 07:27  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 2 replies; 111+ messages in thread

From: Andrey M. Borodin @ 2020-09-16 07:27 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 15 сент. 2020 г., в 22:07, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> regression=#  SELECT f1, f1 <-> '0,1' FROM point_tbl ORDER BY f1 <-> '0,1';
>        f1         |     ?column?
> -------------------+------------------
> (0,0)             |                1
> (1e-300,-1e-300)  |                1
> (-3,4)            | 4.24264068711929
> (-10,0)           | 10.0498756211209
> (10,10)           | 13.4536240470737
> (-5,-12)          | 13.9283882771841
> (5.1,34.5)        |  33.885985303662
> (1e+300,Infinity) |         Infinity
> (NaN,NaN)         |              NaN
>                   |
> (10 rows)
> 
> It is arbitrary which one you get first.
> 
> It's not very nice to have a not-well defined order of rows in the expected output, as it could change in the future if we change the index build algorithm again. But we have plenty of cases that depend on the physical row order, and it's not like this changes very often, so I think it's ok to just memorize the new order in the expected output.


I think this is valid reasoning. GiST choose subtree algorithm is not deterministic, it calls random(), but not in tested paths. 
I was thinking that machine epsilon is near 1e-300, but I was wrong. It's actually near 1e-15.

Actually, I just want to understand what changes between v18 and v19 changed on-page order of items. I look into patch diff and cannot figure it out. There are only logging changes. How this affects scan?

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-16 08:20  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  1 sibling, 0 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-16 08:20 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 16/09/2020 10:27, Andrey M. Borodin wrote:
> Actually, I just want to understand what changes between v18 and v19
> changed on-page order of items. I look into patch diff and cannot
> figure it out. There are only logging changes. How this affects
> scan?

The test was failing with v18 too.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-16 09:09  Kyotaro Horiguchi <horikyota.ntt@gmail.com>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  1 sibling, 0 replies; 111+ messages in thread

From: Kyotaro Horiguchi @ 2020-09-16 09:09 UTC (permalink / raw)
  To: x4mmm@yandex-team.ru; +Cc: hlinnaka@iki.fi; me@komzpa.net; pashkin.elfe@gmail.com; thomas.munro@gmail.com; michael@paquier.xyz; a.korotkov@postgrespro.ru; pgsql-hackers

At Wed, 16 Sep 2020 12:27:09 +0500, "Andrey M. Borodin" <x4mmm@yandex-team.ru> wrote in 
> I was thinking that machine epsilon is near 1e-300, but I was
> wrong. It's actually near 1e-15.

FWIW, the mantissa of double is effectively 52+1 bits, about 15.9
digits. so 1+(1e-16) is basically indistincitve from
1+(2e-16). Actually two double precisions 1+2e-16 and 1+3e-16 are
indistinctive from each other.

> Actually, I just want to understand what changes between v18 and v19 changed on-page order of items. I look into patch diff and cannot figure it out. There are only logging changes. How this affects scan?

FWIW, I saw the same symptom by my another patch after adding a value
to POINT_TBL. (But I didn't pursue the cause further..)

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-17 08:38  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  2 siblings, 2 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-17 08:38 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 15/09/2020 14:36, Heikki Linnakangas wrote:
> Another patch version, fixed a few small bugs pointed out by assertion
> failures in the regression tests.

Pushed. Thanks everyone!

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-17 08:43  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  1 sibling, 0 replies; 111+ messages in thread

From: Andrey M. Borodin @ 2020-09-17 08:43 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 17 сент. 2020 г., в 13:38, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> On 15/09/2020 14:36, Heikki Linnakangas wrote:
>> Another patch version, fixed a few small bugs pointed out by assertion
>> failures in the regression tests.
> 
> Pushed. Thanks everyone!

That's wonderful! Thank you, Heikki!

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-17 12:11  Justin Pryzby <pryzby@telsasoft.com>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  1 sibling, 0 replies; 111+ messages in thread

From: Justin Pryzby @ 2020-09-17 12:11 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Thu, Sep 17, 2020 at 11:38:47AM +0300, Heikki Linnakangas wrote:
> On 15/09/2020 14:36, Heikki Linnakangas wrote:
> > Another patch version, fixed a few small bugs pointed out by assertion
> > failures in the regression tests.
> 
> Pushed. Thanks everyone!

+/* FIXME: bump this before pushing! */
 #define CATALOG_VERSION_NO     202009031






^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-20 20:41  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  2 siblings, 1 reply; 111+ messages in thread

From: Tom Lane @ 2020-09-20 20:41 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

Heikki Linnakangas <hlinnaka@iki.fi> writes:
> Pushed. Thanks everyone!

It appears that hyrax (CLOBBER_CACHE_ALWAYS) is not very happy
with this:

https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2020-09-19%2021%3A27%3A23

We have a recent pass from prion, showing that -DRELCACHE_FORCE_RELEASE
-DCATCACHE_FORCE_RELEASE doesn't cause a problem, so maybe hyrax's
result is just random cosmic rays or something.  But I doubt it.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-20 21:10  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 1 reply; 111+ messages in thread

From: Tom Lane @ 2020-09-20 21:10 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

I wrote:
> It appears that hyrax (CLOBBER_CACHE_ALWAYS) is not very happy
> with this:
> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2020-09-19%2021%3A27%3A23

I reproduced that and traced it to a missing RelationOpenSmgr call.
Fixed now.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-20 22:44  Justin Pryzby <pryzby@telsasoft.com>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 1 reply; 111+ messages in thread

From: Justin Pryzby @ 2020-09-20 22:44 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Andrey M. Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Sun, Sep 20, 2020 at 05:10:05PM -0400, Tom Lane wrote:
> I wrote:
> > It appears that hyrax (CLOBBER_CACHE_ALWAYS) is not very happy
> > with this:
> > https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2020-09-19%2021%3A27%3A23
> 
> I reproduced that and traced it to a missing RelationOpenSmgr call.
> Fixed now.

This also appears to break checksums.

postgres=#  CREATE TABLE pvactst (i INT, a INT[], p POINT) with (autovacuum_enabled = off);
postgres=#  CREATE INDEX gist_pvactst ON pvactst USING gist (p);
postgres=#  INSERT INTO pvactst SELECT i, array[1,2,3], point(i, i+1) FROM generate_series(1,1000) i;
WARNING:  page verification failed, calculated checksum 34313 but expected 0
ERROR:  invalid page in block 0 of relation base/12859/16389

I was able to make this work like so:

@@ -449,6 +450,7 @@ gist_indexsortbuild(GISTBuildState *state)
 
        /* Write out the root */
        PageSetLSN(pagestate->page, GistBuildLSN);
+       PageSetChecksumInplace(pagestate->page, GIST_ROOT_BLKNO, state->indexrel->rd_smgr);
        smgrwrite(state->indexrel->rd_smgr, MAIN_FORKNUM, GIST_ROOT_BLKNO,
                          pagestate->page, true);
        if (RelationNeedsWAL(state->indexrel))
@@ -555,6 +557,7 @@ gist_indexsortbuild_flush_ready_pages(GISTBuildState *state)
 
                PageSetLSN(page, GistBuildLSN);
 
+               PageSetChecksumInplace(page, state->pages_written, state->indexrel->rd_smgr);
                smgrextend(state->indexrel->rd_smgr,
                                   MAIN_FORKNUM,
                                   state->pages_written++,

-- 
Justin





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-20 23:06  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Justin Pryzby <pryzby@telsasoft.com>
  0 siblings, 2 replies; 111+ messages in thread

From: Tom Lane @ 2020-09-20 23:06 UTC (permalink / raw)
  To: Justin Pryzby <pryzby@telsasoft.com>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Andrey M. Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

Justin Pryzby <pryzby@telsasoft.com> writes:
> This also appears to break checksums.

I was wondering about that, because the typical pattern for use of
smgrextend for indexes seems to be

	RelationOpenSmgr(rel);
	PageSetChecksumInplace(page, lastblock);
	smgrextend(rel->rd_smgr, MAIN_FORKNUM, lastblock, zerobuf.data, false);

and gist_indexsortbuild wasn't doing either of the first two things.

gist_indexsortbuild_flush_ready_pages looks like it might be
a few bricks shy of a load too.  But my local CLOBBER_CACHE_ALWAYS
run hasn't gotten to anything except the pretty-trivial index
made in point.sql, so I don't have evidence about it.

Another interesting point is that all the other index AMs seem to WAL-log
the new page before the smgrextend call, whereas this code is doing it
in the other order.  I strongly doubt that both patterns are equally
correct.  Could be that the other AMs are in the wrong though.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-21 08:08  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  1 sibling, 2 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-21 08:08 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; +Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 21/09/2020 02:06, Tom Lane wrote:
> Justin Pryzby <pryzby@telsasoft.com> writes:
>> This also appears to break checksums.

Thanks, I'll go fix it.

> I was wondering about that, because the typical pattern for use of
> smgrextend for indexes seems to be
> 
> 	RelationOpenSmgr(rel);
> 	PageSetChecksumInplace(page, lastblock);
> 	smgrextend(rel->rd_smgr, MAIN_FORKNUM, lastblock, zerobuf.data, false);
> 
> and gist_indexsortbuild wasn't doing either of the first two things.
> 
> gist_indexsortbuild_flush_ready_pages looks like it might be
> a few bricks shy of a load too.  But my local CLOBBER_CACHE_ALWAYS
> run hasn't gotten to anything except the pretty-trivial index
> made in point.sql, so I don't have evidence about it.

I don't think a relcache invalidation can happen on the index we're 
building. Other similar callers call RelationOpenSmgr(rel) before every 
write though (e.g. _bt_blwritepage()), so perhaps it's better to copy 
that pattern here too.

> Another interesting point is that all the other index AMs seem to WAL-log
> the new page before the smgrextend call, whereas this code is doing it
> in the other order.  I strongly doubt that both patterns are equally
> correct.  Could be that the other AMs are in the wrong though.

My thinking was that it's better to call smgrextend() first, so that if 
you run out of disk space, you get the error before WAL-logging it. That 
reduces the chance that WAL replay will run out of disk space. A lot of 
things are different during WAL replay, so it's quite likely that WAL 
replay runs out of disk space anyway if you're living on the edge, but 
still.

I didn't notice that the other callers are doing it the other way round, 
though. I think they need to, so that they can stamp the page with the 
LSN of the WAL record. But GiST build is special in that regard, because 
it stamps all pages with GistBuildLSN.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-21 08:45  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  1 sibling, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-21 08:45 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 21/09/2020 11:08, Heikki Linnakangas wrote:
> I think they need to, so that they can stamp the page with the LSN of
> the WAL record. But GiST build is special in that regard, because it
> stamps all pages with GistBuildLSN.

Actually, don't we have a problem with that, even before this patch? 
Even though we set the LSN to the magic GistBuildLSN value when we build 
the index, WAL replay will write the LSN of the record instead. That 
would mess with the LSN-NSN interlock. After WAL replay (or in a 
streaming replica), a scan on the GiST index might traverse right-links 
unnecessarily.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-21 09:06  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey M. Borodin @ 2020-09-21 09:06 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 21 сент. 2020 г., в 13:45, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> On 21/09/2020 11:08, Heikki Linnakangas wrote:
>> I think they need to, so that they can stamp the page with the LSN of
>> the WAL record. But GiST build is special in that regard, because it
>> stamps all pages with GistBuildLSN.
> 
> Actually, don't we have a problem with that, even before this patch? Even though we set the LSN to the magic GistBuildLSN value when we build the index, WAL replay will write the LSN of the record instead. That would mess with the LSN-NSN interlock. After WAL replay (or in a streaming replica), a scan on the GiST index might traverse right-links unnecessarily.

I think we don't set rightlinks during index build.

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-21 11:56  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  1 sibling, 0 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-21 11:56 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; +Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 21/09/2020 02:06, Tom Lane wrote:
> Justin Pryzby <pryzby@telsasoft.com> writes:
>> This also appears to break checksums.

Fixed, thanks for the report!

> I was wondering about that, because the typical pattern for use of
> smgrextend for indexes seems to be
> 
> 	RelationOpenSmgr(rel);
> 	PageSetChecksumInplace(page, lastblock);
> 	smgrextend(rel->rd_smgr, MAIN_FORKNUM, lastblock, zerobuf.data, false);
> 
> and gist_indexsortbuild wasn't doing either of the first two things.
> 
> gist_indexsortbuild_flush_ready_pages looks like it might be
> a few bricks shy of a load too.  But my local CLOBBER_CACHE_ALWAYS
> run hasn't gotten to anything except the pretty-trivial index
> made in point.sql, so I don't have evidence about it.

I added a RelationOpenSmgr() call there too, although it's not needed 
currently. It seems to be enough to do it before the first smgrextend() 
call. But if you removed or refactored the first call someohow, so it 
was not the first call anymore, it would be easy to miss that you'd 
still need the RelationOpenSmgr() call there. It's more consistent with 
the code in nbtsort.c now, too.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-21 12:15  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-21 12:15 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 21/09/2020 12:06, Andrey M. Borodin wrote:
>> 21 сент. 2020 г., в 13:45, Heikki Linnakangas <hlinnaka@iki.fi>
>> написал(а):
>> 
>> Actually, don't we have a problem with that, even before this
>> patch? Even though we set the LSN to the magic GistBuildLSN value
>> when we build the index, WAL replay will write the LSN of the
>> record instead. That would mess with the LSN-NSN interlock. After
>> WAL replay (or in a streaming replica), a scan on the GiST index
>> might traverse right-links unnecessarily.
> 
> I think we don't set rightlinks during index build.

The new GiST sorting code does not, but the regular insert-based code does.

That's a bit questionable in the new code actually. Was that a conscious
decision? The right-links are only needed when there are concurrent page
splits, so I think it's OK, but the checks for InvalidBlockNumber in
gistScanPage() and gistFindPage() have comment "/* sanity check */".
Comment changes are needed, at least.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-21 13:29  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 2 replies; 111+ messages in thread

From: Andrey M. Borodin @ 2020-09-21 13:29 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 21 сент. 2020 г., в 17:15, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> On 21/09/2020 12:06, Andrey M. Borodin wrote
>> 
>> I think we don't set rightlinks during index build.
> 
> The new GiST sorting code does not, but the regular insert-based code does.
> 
> That's a bit questionable in the new code actually. Was that a conscious
> decision? The right-links are only needed when there are concurrent page
> splits, so I think it's OK, but the checks for InvalidBlockNumber in
> gistScanPage() and gistFindPage() have comment "/* sanity check */".
> Comment changes are needed, at least.

It was a conscious decision with incorrect motivation. I was thinking that it will help to reduce number of "false positive" inspecting right pages. But now I see that:
1. There should be no such "false positives" that we can avoid
2. Valid rightlinks could help to do amcheck verification in future

But thing that bothers me now: when we vacuum leaf page, we bump it's NSN. But we do not bump internal page LSN. Does this means we will follow rightlinks after vacuum? It seems superflous. And btw we did not adjust internal page tuples after vacuum...

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-21 14:19  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  1 sibling, 1 reply; 111+ messages in thread

From: Andrey M. Borodin @ 2020-09-21 14:19 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 21 сент. 2020 г., в 18:29, Andrey M. Borodin <x4mmm@yandex-team.ru> написал(а):
> 
> It was a conscious decision with incorrect motivation. I was thinking that it will help to reduce number of "false positive" inspecting right pages. But now I see that:
> 1. There should be no such "false positives" that we can avoid
> 2. Valid rightlinks could help to do amcheck verification in future

Well, point number 2 here is invalid. There exist one leaf page p, so that if we start traversing rightlink from p we will reach all leaf pages. But we practically have no means to find this page. This makes rightlinks not very helpful in amcheck for GiST.

But for consistency I think it worth to install them.

Thanks!

Best regards, Andrey Borodin.

Attachments:

0001-Install-rightlinks-on-GiST-pages-in-case-of-sorting-.patch (application/octet-stream, 1.8K) download From 92efd0743cd4747fded2df92635e81b80b0b80dd Mon Sep 17 00:00:00 2001 From: Andrey Borodin <amborodin@acm.org> Date: Mon, 21 Sep 2020 19:10:54 +0500 Subject: [PATCH] Install rightlinks on GiST pages in case of sorting build Actually we install reversive links, but GiST pages do not preserve any order. We hold basic invariant: pages within one level construct valid linked list. --- src/backend/access/gist/gistbuild.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index 188e33642f..ed8e66ddb2 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -121,6 +121,7 @@ typedef struct typedef struct GistSortedBuildPageState { Page page; + BlockNumber rightlink; struct GistSortedBuildPageState *parent; /* Upper level, if any */ } GistSortedBuildPageState; @@ -418,6 +419,7 @@ gist_indexsortbuild(GISTBuildState *state) leafstate = palloc(sizeof(GistSortedBuildPageState)); leafstate->page = page; leafstate->parent = NULL; + leafstate->rightlink = InvalidBlockNumber; gistinitpage(page, F_LEAF); /* @@ -531,6 +533,7 @@ gist_indexsortbuild_pagestate_flush(GISTBuildState *state, parent = palloc(sizeof(GistSortedBuildPageState)); parent->page = (Page) palloc(BLCKSZ); parent->parent = NULL; + parent->rightlink = InvalidBlockNumber; gistinitpage(parent->page, 0); pagestate->parent = parent; @@ -540,6 +543,8 @@ gist_indexsortbuild_pagestate_flush(GISTBuildState *state, /* Re-initialize the page buffer for next page on this level. */ pagestate->page = palloc(BLCKSZ); gistinitpage(pagestate->page, isleaf ? F_LEAF : 0); + GistPageGetOpaque(pagestate->page)->rightlink = pagestate->rightlink; + pagestate->rightlink = blkno; } static void -- 2.24.3 (Apple Git-128) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-21 14:20  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  1 sibling, 0 replies; 111+ messages in thread

From: Tom Lane @ 2020-09-21 14:20 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Justin Pryzby <pryzby@telsasoft.com>; Andrey M. Borodin <x4mmm@yandex-team.ru>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

Heikki Linnakangas <hlinnaka@iki.fi> writes:
> On 21/09/2020 02:06, Tom Lane wrote:
>> Another interesting point is that all the other index AMs seem to WAL-log
>> the new page before the smgrextend call, whereas this code is doing it
>> in the other order.  I strongly doubt that both patterns are equally
>> correct.  Could be that the other AMs are in the wrong though.

> My thinking was that it's better to call smgrextend() first, so that if 
> you run out of disk space, you get the error before WAL-logging it. That 
> reduces the chance that WAL replay will run out of disk space. A lot of 
> things are different during WAL replay, so it's quite likely that WAL 
> replay runs out of disk space anyway if you're living on the edge, but 
> still.

Yeah.  access/transam/README points out that such failures need to be
planned for, and explains what we do for heap pages;

    1. Adding a disk page to an existing table.

    This action isn't WAL-logged at all.  We extend a table by writing a page
    of zeroes at its end.  We must actually do this write so that we are sure
    the filesystem has allocated the space.  If the write fails we can just
    error out normally.  Once the space is known allocated, we can initialize
    and fill the page via one or more normal WAL-logged actions.  Because it's
    possible that we crash between extending the file and writing out the WAL
    entries, we have to treat discovery of an all-zeroes page in a table or
    index as being a non-error condition.  In such cases we can just reclaim
    the space for re-use.

So GIST seems to be acting according to that design.  (Someday we need
to update this para to acknowledge that not all filesystems behave as
it's assuming.)

> I didn't notice that the other callers are doing it the other way round, 
> though. I think they need to, so that they can stamp the page with the 
> LSN of the WAL record. But GiST build is special in that regard, because 
> it stamps all pages with GistBuildLSN.

Kind of unpleasant; that means they risk what the README points out:

    In all of these cases, if WAL replay fails to redo the original action
    we must panic and abort recovery.  The DBA will have to manually clean up
    (for instance, free up some disk space or fix directory permissions) and
    then restart recovery.  This is part of the reason for not writing a WAL
    entry until we've successfully done the original action.

I'm not sufficiently motivated to go and change it right now, though.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-28 06:53  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  1 sibling, 0 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-28 06:53 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers


On 21/09/2020 16:29, Andrey M. Borodin wrote:
> But thing that bothers me now: when we vacuum leaf page, we bump it's
> NSN. But we do not bump internal page LSN. Does this means we will
> follow rightlinks after vacuum? It seems superflous.

Sorry, I did not understand what you said above. Vacuum doesn't update 
any NSNs, only LSNs. Can you elaborate?

> And btw we did not adjust internal page tuples after vacuum...
What do you mean by that?

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-28 08:12  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  0 siblings, 2 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-28 08:12 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 21/09/2020 17:19, Andrey M. Borodin wrote:
>> 21 сент. 2020 г., в 18:29, Andrey M. Borodin <x4mmm@yandex-team.ru> написал(а):
>>
>> It was a conscious decision with incorrect motivation. I was thinking that it will help to reduce number of "false positive" inspecting right pages. But now I see that:
>> 1. There should be no such "false positives" that we can avoid
>> 2. Valid rightlinks could help to do amcheck verification in future
> 
> Well, point number 2 here is invalid. There exist one leaf page p, so that if we start traversing rightlink from p we will reach all leaf pages. But we practically have no means to find this page. This makes rightlinks not very helpful in amcheck for GiST.

Well, if you store all the right links in a hash table or something, you 
can "connect the dots" after you have scanned all the pages to see that 
the chain is unbroken. Probably would not be worth the trouble, since 
the rightlinks are not actually needed after concurrent scans have 
completed.

> But for consistency I think it worth to install them.

I agree. I did some testing with your patch. It seems that the 
rightlinks are still not always set. I didn't try to debug why.

I wrote a couple of 'pageinspect' function to inspect GiST pages for 
this. See attached. I then created a test table and index like this:

create table points (p point);
insert into points select point(x,y) from generate_series(-2000, 2000) 
x, generate_series(-2000, 2000) y;
create index points_idx on points using gist (p);

And this is what the root page looks like:

postgres=# select * from gist_page_items(get_raw_page('points_idx', 0));
  itemoffset |     ctid      | itemlen
------------+---------------+---------
           1 | (27891,65535) |      40
           2 | (55614,65535) |      40
           3 | (83337,65535) |      40
           4 | (97019,65535) |      40
(4 rows)

And the right links on the next level:

postgres=# select * from (VALUES (27891), (55614), (83337), (97019)) b 
(blkno), lateral gist_page_opaque_info(get_raw_page('points_idx', blkno));
  blkno | lsn | nsn | rightlink  | flags
-------+-----+-----+------------+-------
  27891 | 0/1 | 0/0 | 4294967295 | {}
  55614 | 0/1 | 0/0 | 4294967295 | {}
  83337 | 0/1 | 0/0 |      27891 | {}
  97019 | 0/1 | 0/0 |      55614 | {}
(4 rows)

I expected there to be only one page with invalid right link, but there 
are two.

- Heikki

Attachments:

0001-Add-functions-to-pageinspect-to-inspect-GiST-indexes.patch (text/x-patch, 7.4K) download From c388e458b196454d3535d84f6f7a617f0f3f819a Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas <heikki.linnakangas@iki.fi> Date: Mon, 28 Sep 2020 11:01:45 +0300 Subject: [PATCH 1/1] Add functions to 'pageinspect' to inspect GiST indexes. --- contrib/pageinspect/Makefile | 4 +- contrib/pageinspect/gistfuncs.c | 173 ++++++++++++++++++ contrib/pageinspect/pageinspect--1.8--1.9.sql | 27 +++ contrib/pageinspect/pageinspect.control | 2 +- 4 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 contrib/pageinspect/gistfuncs.c create mode 100644 contrib/pageinspect/pageinspect--1.8--1.9.sql diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index d9d8177116b..0f9561616e1 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -7,12 +7,14 @@ OBJS = \ btreefuncs.o \ fsmfuncs.o \ ginfuncs.o \ + gistfuncs.o \ hashfuncs.o \ heapfuncs.o \ rawpage.o EXTENSION = pageinspect -DATA = pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \ +DATA = pageinspect--1.8--1.9.sql \ + pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \ pageinspect--1.5.sql pageinspect--1.5--1.6.sql \ pageinspect--1.4--1.5.sql pageinspect--1.3--1.4.sql \ pageinspect--1.2--1.3.sql pageinspect--1.1--1.2.sql \ diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c new file mode 100644 index 00000000000..8517b4c241f --- /dev/null +++ b/contrib/pageinspect/gistfuncs.c @@ -0,0 +1,173 @@ +/* + * gistfuncs.c + * Functions to investigate the content of GiST indexes + * + * Copyright (c) 2014-2020, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/pageinspect/gitfuncs.c + */ +#include "postgres.h" + +#include "access/gist.h" +#include "access/htup.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "pageinspect.h" +#include "storage/itemptr.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/pg_lsn.h" + +PG_FUNCTION_INFO_V1(gist_page_opaque_info); +PG_FUNCTION_INFO_V1(gist_page_items); + +#define ItemPointerGetDatum(X) PointerGetDatum(X) + + +Datum +gist_page_opaque_info(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + TupleDesc tupdesc; + Page page; + GISTPageOpaque opaq; + HeapTuple resultTuple; + Datum values[4]; + bool nulls[4]; + Datum flags[16]; + int nflags = 0; + uint16 flagbits; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + page = get_page_from_raw(raw_page); + + opaq = (GISTPageOpaque) PageGetSpecialPointer(page); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Convert the flags bitmask to an array of human-readable names */ + flagbits = opaq->flags; + if (flagbits & F_LEAF) + flags[nflags++] = CStringGetTextDatum("leaf"); + if (flagbits & F_DELETED) + flags[nflags++] = CStringGetTextDatum("deleted"); + if (flagbits & F_TUPLES_DELETED) + flags[nflags++] = CStringGetTextDatum("tuples_deleted"); + if (flagbits & F_FOLLOW_RIGHT) + flags[nflags++] = CStringGetTextDatum("follow_right"); + if (flagbits & F_HAS_GARBAGE) + flags[nflags++] = CStringGetTextDatum("has_garbage"); + flagbits &= ~(F_LEAF | F_DELETED | F_TUPLES_DELETED | F_FOLLOW_RIGHT | F_HAS_GARBAGE); + if (flagbits) + { + /* any flags we don't recognize are printed in hex */ + flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits)); + } + + memset(nulls, 0, sizeof(nulls)); + + values[0] = LSNGetDatum(PageGetLSN(page)); + values[1] = LSNGetDatum(GistPageGetNSN(page)); + values[2] = Int64GetDatum(opaq->rightlink); + values[3] = PointerGetDatum(construct_array(flags, nflags, + TEXTOID, + -1, false, TYPALIGN_INT)); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(tupdesc, values, nulls); + + return HeapTupleGetDatum(resultTuple); +} + +typedef struct gist_page_items_state +{ + Page page; + TupleDesc tupd; + OffsetNumber offset; +} gist_page_items_state; + +Datum +gist_page_items(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + FuncCallContext *fctx; + gist_page_items_state *inter_call_data; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + if (SRF_IS_FIRSTCALL()) + { + TupleDesc tupdesc; + MemoryContext mctx; + Page page; + + fctx = SRF_FIRSTCALL_INIT(); + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + page = get_page_from_raw(raw_page); + + inter_call_data = palloc(sizeof(gist_page_items_state)); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + inter_call_data->page = page; + inter_call_data->tupd = tupdesc; + inter_call_data->offset = FirstOffsetNumber; + + fctx->max_calls = PageGetMaxOffsetNumber(page); + fctx->user_fctx = inter_call_data; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + inter_call_data = fctx->user_fctx; + + if (fctx->call_cntr < fctx->max_calls) + { + Page page = inter_call_data->page; + OffsetNumber offset = inter_call_data->offset; + HeapTuple resultTuple; + Datum result; + Datum values[3]; + bool nulls[3]; + ItemId id; + IndexTuple itup; + + id = PageGetItemId(page, offset); + + if (!ItemIdIsValid(id)) + elog(ERROR, "invalid ItemId"); + + itup = (IndexTuple) PageGetItem(page, id); + + memset(nulls, 0, sizeof(nulls)); + + values[0] = DatumGetInt16(offset); + values[1] = ItemPointerGetDatum(&itup->t_tid); + values[2] = Int32GetDatum((int) IndexTupleSize(itup)); + + /* TODO: also print the keys */ + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + inter_call_data->offset++; + SRF_RETURN_NEXT(fctx, result); + } + + SRF_RETURN_DONE(fctx); +} diff --git a/contrib/pageinspect/pageinspect--1.8--1.9.sql b/contrib/pageinspect/pageinspect--1.8--1.9.sql new file mode 100644 index 00000000000..e7a849cfedf --- /dev/null +++ b/contrib/pageinspect/pageinspect--1.8--1.9.sql @@ -0,0 +1,27 @@ +/* contrib/pageinspect/pageinspect--1.8--1.9.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.9'" to load this file. \quit + +-- +-- gist_page_opaque_info() +-- +CREATE FUNCTION gist_page_opaque_info(IN page bytea, + OUT lsn pg_lsn, + OUT nsn pg_lsn, + OUT rightlink bigint, + OUT flags text[]) +AS 'MODULE_PATHNAME', 'gist_page_opaque_info' +LANGUAGE C STRICT PARALLEL SAFE; + + +-- +-- gist_page_items() +-- +CREATE FUNCTION gist_page_items(IN page bytea, + OUT itemoffset smallint, + OUT ctid tid, + OUT itemlen smallint) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'gist_page_items' +LANGUAGE C STRICT PARALLEL SAFE; diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control index f8cdf526c65..bd716769a17 100644 --- a/contrib/pageinspect/pageinspect.control +++ b/contrib/pageinspect/pageinspect.control @@ -1,5 +1,5 @@ # pageinspect extension comment = 'inspect the contents of database pages at a low level' -default_version = '1.8' +default_version = '1.9' module_pathname = '$libdir/pageinspect' relocatable = true -- 2.20.1
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-29 18:04  Andrey M. Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  1 sibling, 2 replies; 111+ messages in thread

From: Andrey M. Borodin @ 2020-09-29 18:04 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 28 сент. 2020 г., в 13:12, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> On 21/09/2020 17:19, Andrey M. Borodin wrote:
>>> 21 сент. 2020 г., в 18:29, Andrey M. Borodin <x4mmm@yandex-team.ru> написал(а):
>>> 
>>> It was a conscious decision with incorrect motivation. I was thinking that it will help to reduce number of "false positive" inspecting right pages. But now I see that:
>>> 1. There should be no such "false positives" that we can avoid
>>> 2. Valid rightlinks could help to do amcheck verification in future
>> Well, point number 2 here is invalid. There exist one leaf page p, so that if we start traversing rightlink from p we will reach all leaf pages. But we practically have no means to find this page. This makes rightlinks not very helpful in amcheck for GiST.
> 
> Well, if you store all the right links in a hash table or something, you can "connect the dots" after you have scanned all the pages to see that the chain is unbroken. Probably would not be worth the trouble, since the rightlinks are not actually needed after concurrent scans have completed.
> 
>> But for consistency I think it worth to install them.
> 
> I agree. I did some testing with your patch. It seems that the rightlinks are still not always set. I didn't try to debug why.
> 
> I wrote a couple of 'pageinspect' function to inspect GiST pages for this. See attached. I then created a test table and index like this:
> 
> create table points (p point);
> insert into points select point(x,y) from generate_series(-2000, 2000) x, generate_series(-2000, 2000) y;
> create index points_idx on points using gist (p);
> 
> And this is what the root page looks like:
> 
> postgres=# select * from gist_page_items(get_raw_page('points_idx', 0));
> itemoffset |     ctid      | itemlen
> ------------+---------------+---------
>          1 | (27891,65535) |      40
>          2 | (55614,65535) |      40
>          3 | (83337,65535) |      40
>          4 | (97019,65535) |      40
> (4 rows)
> 
> And the right links on the next level:
> 
> postgres=# select * from (VALUES (27891), (55614), (83337), (97019)) b (blkno), lateral gist_page_opaque_info(get_raw_page('points_idx', blkno));
> blkno | lsn | nsn | rightlink  | flags
> -------+-----+-----+------------+-------
> 27891 | 0/1 | 0/0 | 4294967295 | {}
> 55614 | 0/1 | 0/0 | 4294967295 | {}
> 83337 | 0/1 | 0/0 |      27891 | {}
> 97019 | 0/1 | 0/0 |      55614 | {}
> (4 rows)
> 
> I expected there to be only one page with invalid right link, but there are two.

Yes, there is a bug. Now it seems to me so obvious, yet it took some time to understand that links were shifted by one extra jump. PFA fixed rightlinks installation.

BTW some one more small thing: we initialise page buffers with palloc() and palloc0(), while first one is sufficient for gistinitpage().
Also I'm working on btree_gist opclasses and found out that new sortsupport function is not mentioned in gistadjustmembers(). I think this can be fixed with gist_btree patch.

Your pageinspect patch seems very useful. How do you think, should we provide a way to find invalid tuples in GiST within gist_page_items()? At some point we will have to ask user to reindex GiSTs with invalid tuples.


> 28 сент. 2020 г., в 11:53, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> On 21/09/2020 16:29, Andrey M. Borodin wrote:
>> But thing that bothers me now: when we vacuum leaf page, we bump it's
>> NSN. But we do not bump internal page LSN. Does this means we will
>> follow rightlinks after vacuum? It seems superflous.
> 
> Sorry, I did not understand what you said above. Vacuum doesn't update any NSNs, only LSNs. Can you elaborate?
I've misunderstood difference between NSN and LSN. Seems like everything is fine.

> 
>> And btw we did not adjust internal page tuples after vacuum...
> What do you mean by that?

When we delete rows from table internal tuples in GiST stay wide.


Thanks!

Best regards, Andrey Borodin.

Attachments:

install_rightlinks_v2.diff (application/octet-stream, 517B) download diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index 188e33642f..aaf1a4a146 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -540,6 +540,7 @@ gist_indexsortbuild_pagestate_flush(GISTBuildState *state, /* Re-initialize the page buffer for next page on this level. */ pagestate->page = palloc(BLCKSZ); gistinitpage(pagestate->page, isleaf ? F_LEAF : 0); + GistPageGetOpaque(pagestate->page)->rightlink = blkno; } static void
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-09-29 20:31  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  1 sibling, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2020-09-29 20:31 UTC (permalink / raw)
  To: Andrey M. Borodin <x4mmm@yandex-team.ru>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 29/09/2020 21:04, Andrey M. Borodin wrote:
> 28 сент. 2020 г., в 13:12, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
>> I did some testing with your patch. It seems that the rightlinks
>> are still not always set. I didn't try to debug why.
> 
> Yes, there is a bug. Now it seems to me so obvious, yet it took some 
> time to understand that links were shifted by one extra jump. PFA 
> fixed rightlinks installation.
Ah, that was simple. I propose adding a comment on it:

inline patch 1: src/backend/access/gist/gistbuild.c (+0/-0 lines) download inline-1.patch--- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -540,6 +540,19 @@ gist_indexsortbuild_pagestate_flush(GISTBuildState
*state, /* Re-initialize the page buffer for next page on this level. */ pagestate->page = palloc(BLCKSZ); gistinitpage(pagestate->page, isleaf ? F_LEAF : 0); + + /* + * Set the right link to point to the previous page. This is just for + * debugging purposes: GiST only follows the right link if a page is split + * concurrently to a scan, and that cannot happen during index build. + * + * It's a bit counterintuitive that we set the right link on the new page + * to point to the previous page, and not the other way round. But GiST + * pages are not ordered like B-tree pages are, so as long as the + * right-links form a chain through all the pages in the same level, the + * order doesn't matter. + */ + GistPageGetOpaque(pagestate->page)->rightlink = blkno; } > BTW some one more small thing: we initialise page buffers with > palloc() and palloc0(), while first one is sufficient for > gistinitpage(). Hmm. Only the first one, in gist_indexsortbuild(), but that needs to be palloc0, because it's used to write an all-zeros placeholder for the root page. > Also I'm working on btree_gist opclasses and found out that new > sortsupport function is not mentioned in gistadjustmembers(). I think > this can be fixed with gist_btree patch. Thanks! > Your pageinspect patch seems very useful. How do you think, should we > provide a way to find invalid tuples in GiST within > gist_page_items()? At some point we will have to ask user to reindex > GiSTs with invalid tuples. You mean invalid tuples created by crash on PostgreSQL version 9.0 or below, and pg_upgraded? I doubt there are any of those still around in the wild. We have to keep the code to detect them, though. It would be nice to improve gist_page_items() to display more information about the items, although I wouldn't worry much about invalid tuples. The 'gevel' extension that Oleg mentioned upthread does more, it would be nice to incorporate that into pageinspect somehow. - Heikki ^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-10-06 11:05  Pavel Borisov <pashkin.elfe@gmail.com>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 2 replies; 111+ messages in thread

From: Pavel Borisov @ 2020-10-06 11:05 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

I've been making tests with memory sanitizer and got one another error in
regression test create_index:
CREATE INDEX gpointind ON point_tbl USING gist (f1);
server closed the connection unexpectedly

with logfile:
gistproc.c:1714:28: runtime error: 1e+300 is outside the range of
representable values of type 'float'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior gistproc.c:1714:28

gistproc.c:
1714     z = point_zorder_internal(p->x, p->y);

Consider this a minor issue but unrelated to the other issues discussed. It
is reproduced on the last master
commit 0a3c864c32751fd29d021929cf70af421fd27370 after all changes into Gist
committed.

cflags="-DUSE_VALGRIND -Og -O0 -fsanitize=address -fsanitize=undefined
-fno-sanitize-recover=all -fno-sanitize=alignment -fstack-protector"


-- 
Best regards,
Pavel Borisov

Postgres Professional: http://postgrespro.com <http://www.postgrespro.com;

^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-10-06 13:19  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Pavel Borisov <pashkin.elfe@gmail.com>
  1 sibling, 0 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2020-10-06 13:19 UTC (permalink / raw)
  To: Pavel Borisov <pashkin.elfe@gmail.com>; +Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 06/10/2020 14:05, Pavel Borisov wrote:
> I've been making tests with memory sanitizer

Thanks!

> and got one another error 
> in regression test create_index:
> CREATE INDEX gpointind ON point_tbl USING gist (f1);
> server closed the connection unexpectedly
> 
> with logfile:
> gistproc.c:1714:28: runtime error: 1e+300 is outside the range of 
> representable values of type 'float'
> SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior gistproc.c:1714:28
> 
> gistproc.c:
> 1714     z = point_zorder_internal(p->x, p->y);
> 
> Consider this a minor issue but unrelated to the other issues discussed. 
> It is reproduced on the last master 
> commit 0a3c864c32751fd29d021929cf70af421fd27370 after all changes into 
> Gist committed.
> 
> cflags="-DUSE_VALGRIND -Og -O0 -fsanitize=address -fsanitize=undefined 
> -fno-sanitize-recover=all -fno-sanitize=alignment -fstack-protector"

You get the same error with:

select (float8 '1e+300')::float4;

float.c:1204:11: runtime error: 1e+300 is outside the range of 
representable values of type 'float'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior float.c:1204:11 in

It boils down to casting a C double to float, when the value doesn't fit 
in float. I'm surprised that's undefined behavior, but I'm no C99 
lawyer. The code in dtof() expects it to yield Inf.

I'm inclined to shrug this off and say that the sanitizer is being 
over-zealous. Is there some compiler flag we should be setting, to tell 
it that we require specific behavior? Any other ideas?

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-10-06 14:29  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Pavel Borisov <pashkin.elfe@gmail.com>
  1 sibling, 1 reply; 111+ messages in thread

From: Tom Lane @ 2020-10-06 14:29 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Pavel Borisov <pashkin.elfe@gmail.com>; Andrey M. Borodin <x4mmm@yandex-team.ru>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

Heikki Linnakangas <hlinnaka@iki.fi> writes:
> You get the same error with:
> select (float8 '1e+300')::float4;
> float.c:1204:11: runtime error: 1e+300 is outside the range of 
> representable values of type 'float'
> SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior float.c:1204:11 in

> It boils down to casting a C double to float, when the value doesn't fit 
> in float. I'm surprised that's undefined behavior, but I'm no C99 
> lawyer. The code in dtof() expects it to yield Inf.

I think UBSan read C99 6.3.1.5:

       [#2]  When  a double is demoted to float or a long double to
       double or float, if the value being converted is outside the
       range  of  values  that  can be represented, the behavior is
       undefined.

and stopped reading at that point, which they should not have.
If you go on to read the portions around, particularly, <fenv.h>,
you get a different picture of affairs.  If we're relying on IEEE
float semantics in other places, which we are, we're perfectly
entitled to assume that the cast will yield Inf (and a floating
point exception flag, which we ignore).  I think the "undefined"
here is just meant to say that there's no single behavior promised
across all possible C implementations.  They'd have been better to
write "implementation-defined", though.

> I'm inclined to shrug this off and say that the sanitizer is being 
> over-zealous. Is there some compiler flag we should be setting, to tell 
> it that we require specific behavior? Any other ideas?

If UBSan doesn't have a flag to tell it to assume IEEE math,
I'd say that makes it next door to worthless for our purposes.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-10-06 15:29  Pavel Borisov <pashkin.elfe@gmail.com>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 0 replies; 111+ messages in thread

From: Pavel Borisov @ 2020-10-06 15:29 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Andrey M. Borodin <x4mmm@yandex-team.ru>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

It became normal with
-fsanitize=signed-integer-overflow,null,alignment
instead of
-fsanitize=undefined
(which is strictly a 'default' list of needed and unnecessary things to
check, can be overridden anyway but needed some reading for it)

Thanks!

-- 
Best regards,
Pavel Borisov

Postgres Professional: http://postgrespro.com <http://www.postgrespro.com;

^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-10-07 12:27  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Andrey M. Borodin <x4mmm@yandex-team.ru>
  1 sibling, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2020-10-07 12:27 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 29 сент. 2020 г., в 23:04, Andrey M. Borodin <x4mmm@yandex-team.ru> написал(а):
> 
> 
> Also I'm working on btree_gist opclasses and found out that new sortsupport function is not mentioned in gistadjustmembers(). I think this can be fixed with gist_btree patch.

Here's draft patch with implementation of sortsupport for ints and floats.

Thanks!

Best regards, Andrey Borodin.

Attachments:

0001-Sortsupport-for-sorting-GiST-build-for-ints-and-floa.patch (application/octet-stream, 14.5K) download From d7c3eabb0936170f46ffe4573684123af964b59a Mon Sep 17 00:00:00 2001 From: Andrey Borodin <amborodin@acm.org> Date: Sat, 3 Oct 2020 21:08:54 +0500 Subject: [PATCH] Sortsupport for sorting GiST build for ints and floats --- contrib/btree_gist/Makefile | 2 +- contrib/btree_gist/btree_float4.c | 70 ++++++++++++++++++ contrib/btree_gist/btree_float8.c | 82 +++++++++++++++++++++ contrib/btree_gist/btree_gist--1.6--1.7.sql | 45 +++++++++++ contrib/btree_gist/btree_gist.control | 2 +- contrib/btree_gist/btree_gist.h | 1 + contrib/btree_gist/btree_int2.c | 69 +++++++++++++++++ contrib/btree_gist/btree_int4.c | 69 +++++++++++++++++ contrib/btree_gist/btree_int8.c | 74 ++++++++++++++++++- src/backend/access/gist/gistvalidate.c | 1 + 10 files changed, 412 insertions(+), 3 deletions(-) create mode 100644 contrib/btree_gist/btree_gist--1.6--1.7.sql diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile index e92d974a1a..a1f818f71e 100644 --- a/contrib/btree_gist/Makefile +++ b/contrib/btree_gist/Makefile @@ -32,7 +32,7 @@ EXTENSION = btree_gist DATA = btree_gist--1.0--1.1.sql \ btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \ - btree_gist--1.5--1.6.sql + btree_gist--1.5--1.6.sql btree_gist--1.6--1.7.sql PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes" REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \ diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c index 3604c73313..b83fdd4df3 100644 --- a/contrib/btree_gist/btree_float4.c +++ b/contrib/btree_gist/btree_float4.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float4_consistent); PG_FUNCTION_INFO_V1(gbt_float4_distance); PG_FUNCTION_INFO_V1(gbt_float4_penalty); PG_FUNCTION_INFO_V1(gbt_float4_same); +PG_FUNCTION_INFO_V1(gbt_float4_sortsupport); static bool gbt_float4gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -209,3 +210,72 @@ gbt_float4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + + +static int +gbt_float4_cmp(Datum a, Datum b, SortSupport ssup) +{ + float4KEY *ia = (float4KEY *) DatumGetPointer(a); + float4KEY *ib = (float4KEY *) DatumGetPointer(b); + + if (ia->lower == ib->lower) + { + if (ia->upper == ib->upper) + return 0; + + return (ia->upper > ib->upper) ? 1 : -1; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float4_abbrev_convert(Datum original, SortSupport ssup) +{ + float4KEY *b1 = (float4KEY *) DatumGetPointer(original); + float4 z = b1->lower; + + return (Datum) Float4GetDatum(z); +} + +static int +gbt_float4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (DatumGetFloat4(z1) > DatumGetFloat4(z2)) + return 1; + else if (DatumGetFloat4(z1) < DatumGetFloat4(z2)) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_float4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float4_cmp_abbrev; + ssup->abbrev_converter = gbt_float4_abbrev_convert; + ssup->abbrev_abort = gbt_float4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float4_cmp; + } + else + { + ssup->comparator = gbt_float4_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c index 10a5262aaa..2232071c22 100644 --- a/contrib/btree_gist/btree_float8.c +++ b/contrib/btree_gist/btree_float8.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float8_consistent); PG_FUNCTION_INFO_V1(gbt_float8_distance); PG_FUNCTION_INFO_V1(gbt_float8_penalty); PG_FUNCTION_INFO_V1(gbt_float8_same); +PG_FUNCTION_INFO_V1(gbt_float8_sortsupport); static bool @@ -216,3 +217,84 @@ gbt_float8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + + +static int +gbt_float8_cmp(Datum a, Datum b, SortSupport ssup) +{ + float8KEY *ia = (float8KEY *) DatumGetPointer(a); + float8KEY *ib = (float8KEY *) DatumGetPointer(b); + + if (ia->lower == ib->lower) + { + if (ia->upper == ib->upper) + return 0; + + return (ia->upper > ib->upper) ? 1 : -1; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float8_abbrev_convert(Datum original, SortSupport ssup) +{ + float8KEY *b1 = (float8KEY *) DatumGetPointer(original); + float8 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) Float8GetDatum(z); +#else + return (Datum) 0; +#endif +} + +static int +gbt_float8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + if (DatumGetFloat8(z1) > DatumGetFloat8(z2)) + return 1; + else if (DatumGetFloat8(z1) < DatumGetFloat8(z2)) + return -1; + else + return 0; +#else + return 0; +#endif +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_float8_abbrev_abort(int memtupcount, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + return false; +#else + return true; +#endif +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float8_cmp_abbrev; + ssup->abbrev_converter = gbt_float8_abbrev_convert; + ssup->abbrev_abort = gbt_float8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float8_cmp; + } + else + { + ssup->comparator = gbt_float8_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_gist--1.6--1.7.sql b/contrib/btree_gist/btree_gist--1.6--1.7.sql new file mode 100644 index 0000000000..db10dc575c --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.6--1.7.sql @@ -0,0 +1,45 @@ +/* contrib/btree_gist/btree_gist--1.6--1.7.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.7'" to load this file. \quit + + +CREATE FUNCTION gbt_int8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD + FUNCTION 11 (int8, int8) gbt_int8_sortsupport (internal) ; + +CREATE FUNCTION gbt_int4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD + FUNCTION 11 (int4, int4) gbt_int4_sortsupport (internal) ; + +CREATE FUNCTION gbt_int2_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD + FUNCTION 11 (int2, int2) gbt_int2_sortsupport (internal) ; + +CREATE FUNCTION gbt_float8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD + FUNCTION 11 (float8, float8) gbt_float8_sortsupport (internal) ; + +CREATE FUNCTION gbt_float4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD + FUNCTION 11 (float4, float4) gbt_float4_sortsupport (internal) ; diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control index e5c41fe8f3..fa9171a80a 100644 --- a/contrib/btree_gist/btree_gist.control +++ b/contrib/btree_gist/btree_gist.control @@ -1,6 +1,6 @@ # btree_gist extension comment = 'support for indexing common datatypes in GiST' -default_version = '1.6' +default_version = '1.7' module_pathname = '$libdir/btree_gist' relocatable = true trusted = true diff --git a/contrib/btree_gist/btree_gist.h b/contrib/btree_gist/btree_gist.h index 14c7c8ee19..35ad287ed3 100644 --- a/contrib/btree_gist/btree_gist.h +++ b/contrib/btree_gist/btree_gist.h @@ -6,6 +6,7 @@ #include "access/nbtree.h" #include "fmgr.h" +#include "utils/sortsupport.h" #define BtreeGistNotEqualStrategyNumber 6 diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c index a91b95ff39..8329bf4d3f 100644 --- a/contrib/btree_gist/btree_int2.c +++ b/contrib/btree_gist/btree_int2.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int2_consistent); PG_FUNCTION_INFO_V1(gbt_int2_distance); PG_FUNCTION_INFO_V1(gbt_int2_penalty); PG_FUNCTION_INFO_V1(gbt_int2_same); +PG_FUNCTION_INFO_V1(gbt_int2_sortsupport); static bool gbt_int2gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -214,3 +215,71 @@ gbt_int2_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int2_cmp(Datum a, Datum b, SortSupport ssup) +{ + int16KEY *ia = (int16KEY *) DatumGetPointer(a); + int16KEY *ib = (int16KEY *) DatumGetPointer(b); + + if (ia->lower == ib->lower) + { + if (ia->upper == ib->upper) + return 0; + + return (ia->upper > ib->upper) ? 1 : -1; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int2_abbrev_convert(Datum original, SortSupport ssup) +{ + int16KEY *b1 = (int16KEY *) DatumGetPointer(original); + int16 z = b1->lower; + + return (Datum) z; +} + +static int +gbt_int2_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int2_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int2_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int2_cmp_abbrev; + ssup->abbrev_converter = gbt_int2_abbrev_convert; + ssup->abbrev_abort = gbt_int2_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int2_cmp; + } + else + { + ssup->comparator = gbt_int2_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c index 7ea98c478c..363b25f794 100644 --- a/contrib/btree_gist/btree_int4.c +++ b/contrib/btree_gist/btree_int4.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int4_consistent); PG_FUNCTION_INFO_V1(gbt_int4_distance); PG_FUNCTION_INFO_V1(gbt_int4_penalty); PG_FUNCTION_INFO_V1(gbt_int4_same); +PG_FUNCTION_INFO_V1(gbt_int4_sortsupport); static bool @@ -215,3 +216,71 @@ gbt_int4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int4_cmp(Datum a, Datum b, SortSupport ssup) +{ + int32KEY *ia = (int32KEY *) DatumGetPointer(a); + int32KEY *ib = (int32KEY *) DatumGetPointer(b); + + if (ia->lower == ib->lower) + { + if (ia->upper == ib->upper) + return 0; + + return (ia->upper > ib->upper) ? 1 : -1; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int4_abbrev_convert(Datum original, SortSupport ssup) +{ + int32KEY *b1 = (int32KEY *) DatumGetPointer(original); + int32 z = b1->lower; + + return (Datum) z; +} + +static int +gbt_int4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int4_cmp_abbrev; + ssup->abbrev_converter = gbt_int4_abbrev_convert; + ssup->abbrev_abort = gbt_int4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int4_cmp; + } + else + { + ssup->comparator = gbt_int4_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c index df2b0d174b..2160cf4ad4 100644 --- a/contrib/btree_gist/btree_int8.c +++ b/contrib/btree_gist/btree_int8.c @@ -24,7 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int8_consistent); PG_FUNCTION_INFO_V1(gbt_int8_distance); PG_FUNCTION_INFO_V1(gbt_int8_penalty); PG_FUNCTION_INFO_V1(gbt_int8_same); - +PG_FUNCTION_INFO_V1(gbt_int8_sortsupport); static bool gbt_int8gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -215,3 +215,75 @@ gbt_int8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int8_cmp(Datum a, Datum b, SortSupport ssup) +{ + int64KEY *ia = (int64KEY *) DatumGetPointer(a); + int64KEY *ib = (int64KEY *) DatumGetPointer(b); + + if (ia->lower == ib->lower) + { + if (ia->upper == ib->upper) + return 0; + + return (ia->upper > ib->upper) ? 1 : -1; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int8_abbrev_convert(Datum original, SortSupport ssup) +{ + int64KEY *b1 = (int64KEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gbt_int8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int8_cmp_abbrev; + ssup->abbrev_converter = gbt_int8_abbrev_convert; + ssup->abbrev_abort = gbt_int8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int8_cmp; + } + else + { + ssup->comparator = gbt_int8_cmp; + } + PG_RETURN_VOID(); +} diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index 8a14620fab..e600015b12 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -338,6 +338,7 @@ gistadjustmembers(Oid opfamilyoid, case GIST_DISTANCE_PROC: case GIST_FETCH_PROC: case GIST_OPTIONS_PROC: + case GIST_SORTSUPPORT_PROC: /* Optional, so force it to be a soft family dependency */ op->ref_is_hard = false; op->ref_is_family = true; -- 2.24.3 (Apple Git-128) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-10-07 12:38  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2020-10-07 12:38 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 07/10/2020 15:27, Andrey Borodin wrote:
> Here's draft patch with implementation of sortsupport for ints and floats.

> +static int
> +gbt_int4_cmp(Datum a, Datum b, SortSupport ssup)
> +{
> +	int32KEY   *ia = (int32KEY *) DatumGetPointer(a);
> +	int32KEY   *ib = (int32KEY *) DatumGetPointer(b);
> +
> +	if (ia->lower == ib->lower)
> +	{
> +		if (ia->upper == ib->upper)
> +			return 0;
> +
> +		return (ia->upper > ib->upper) ? 1 : -1;
> +	}
> +
> +	return (ia->lower > ib->lower) ? 1 : -1;
> +}

We're only dealing with leaf items during index build, so the 'upper' 
and 'lower' should always be equal here, right? Maybe add a comment and 
an assertion on that.

(It's pretty sad that the on-disk representation is identical for leaf 
and internal items, because that wastes a lot of disk space, but that's 
way out of scope for this patch.)

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-10-21 17:13  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 2 replies; 111+ messages in thread

From: Andrey Borodin @ 2020-10-21 17:13 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 7 окт. 2020 г., в 17:38, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> On 07/10/2020 15:27, Andrey Borodin wrote:
>> Here's draft patch with implementation of sortsupport for ints and floats.
> 
>> +static int
>> +gbt_int4_cmp(Datum a, Datum b, SortSupport ssup)
>> +{
>> +	int32KEY   *ia = (int32KEY *) DatumGetPointer(a);
>> +	int32KEY   *ib = (int32KEY *) DatumGetPointer(b);
>> +
>> +	if (ia->lower == ib->lower)
>> +	{
>> +		if (ia->upper == ib->upper)
>> +			return 0;
>> +
>> +		return (ia->upper > ib->upper) ? 1 : -1;
>> +	}
>> +
>> +	return (ia->lower > ib->lower) ? 1 : -1;
>> +}
> 
> We're only dealing with leaf items during index build, so the 'upper' and 'lower' should always be equal here, right? Maybe add a comment and an assertion on that.
> 
> (It's pretty sad that the on-disk representation is identical for leaf and internal items, because that wastes a lot of disk space, but that's way out of scope for this patch.)

Thanks, I've added assert() where is was easy to test equalty.

PFA patch with all types.

I had a plan to implement and test one type each day. I did not quite understood how rich our type system is.

Thanks!

Best regards, Andrey Borodin.

Attachments:

0001-Sortsupport-for-sorting-GiST-build-for-gist_btree-ty.patch (application/octet-stream, 44.6K) download From abc2c61d0159203079e511bd6b67f65108b1869a Mon Sep 17 00:00:00 2001 From: Andrey Borodin <amborodin@acm.org> Date: Sat, 3 Oct 2020 21:08:54 +0500 Subject: [PATCH] Sortsupport for sorting GiST build for gist_btree types --- contrib/btree_gist/Makefile | 2 +- contrib/btree_gist/btree_bit.c | 53 ++++++ contrib/btree_gist/btree_bytea.c | 54 +++++- contrib/btree_gist/btree_cash.c | 75 +++++++++ contrib/btree_gist/btree_date.c | 63 +++++++ contrib/btree_gist/btree_enum.c | 70 ++++++++ contrib/btree_gist/btree_float4.c | 71 ++++++++ contrib/btree_gist/btree_float8.c | 79 +++++++++ contrib/btree_gist/btree_gist--1.6--1.7.sql | 176 ++++++++++++++++++++ contrib/btree_gist/btree_gist.control | 2 +- contrib/btree_gist/btree_gist.h | 1 + contrib/btree_gist/btree_inet.c | 80 +++++++++ contrib/btree_gist/btree_int2.c | 70 ++++++++ contrib/btree_gist/btree_int4.c | 70 ++++++++ contrib/btree_gist/btree_int8.c | 75 ++++++++- contrib/btree_gist/btree_macaddr.c | 63 +++++++ contrib/btree_gist/btree_macaddr8.c | 62 +++++++ contrib/btree_gist/btree_numeric.c | 52 ++++++ contrib/btree_gist/btree_oid.c | 71 ++++++++ contrib/btree_gist/btree_text.c | 54 ++++++ contrib/btree_gist/btree_time.c | 60 +++++++ contrib/btree_gist/btree_ts.c | 59 +++++++ contrib/btree_gist/btree_uuid.c | 54 ++++++ src/backend/access/gist/gistvalidate.c | 1 + 24 files changed, 1413 insertions(+), 4 deletions(-) create mode 100644 contrib/btree_gist/btree_gist--1.6--1.7.sql diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile index e92d974a1a..a1f818f71e 100644 --- a/contrib/btree_gist/Makefile +++ b/contrib/btree_gist/Makefile @@ -32,7 +32,7 @@ EXTENSION = btree_gist DATA = btree_gist--1.0--1.1.sql \ btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \ - btree_gist--1.5--1.6.sql + btree_gist--1.5--1.6.sql btree_gist--1.6--1.7.sql PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes" REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \ diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c index 2225244ded..752a320ede 100644 --- a/contrib/btree_gist/btree_bit.c +++ b/contrib/btree_gist/btree_bit.c @@ -19,6 +19,7 @@ PG_FUNCTION_INFO_V1(gbt_bit_picksplit); PG_FUNCTION_INFO_V1(gbt_bit_consistent); PG_FUNCTION_INFO_V1(gbt_bit_penalty); PG_FUNCTION_INFO_V1(gbt_bit_same); +PG_FUNCTION_INFO_V1(gbt_bit_sortsupport); /* define for comparison */ @@ -209,3 +210,55 @@ gbt_bit_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bit_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +static Datum +gbt_bit_abbrev_convert(Datum original, SortSupport ssup) +{ + return (Datum) 0; +} + +static int +gbt_bit_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + return 0; +} + +/* + * We are always aborting the abbreviation. + */ +static bool +gbt_bit_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return true; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bit_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_bit_cmp_abbrev; + ssup->abbrev_converter = gbt_bit_abbrev_convert; + ssup->abbrev_abort = gbt_bit_abbrev_abort; + ssup->abbrev_full_comparator = gbt_bit_cmp_sort; + } + else + { + ssup->comparator = gbt_bit_cmp_sort; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_bytea.c b/contrib/btree_gist/btree_bytea.c index 6b005f0157..67e4a42f60 100644 --- a/contrib/btree_gist/btree_bytea.c +++ b/contrib/btree_gist/btree_bytea.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_bytea_picksplit); PG_FUNCTION_INFO_V1(gbt_bytea_consistent); PG_FUNCTION_INFO_V1(gbt_bytea_penalty); PG_FUNCTION_INFO_V1(gbt_bytea_same); +PG_FUNCTION_INFO_V1(gbt_bytea_sortsupport); /* define for comparison */ @@ -87,7 +88,7 @@ static const gbtree_vinfo tinfo = /************************************************** - * Text ops + * Bytea ops **************************************************/ @@ -168,3 +169,54 @@ gbt_bytea_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bytea_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +static Datum +gbt_bytea_abbrev_convert(Datum original, SortSupport ssup) +{ + return (Datum) 0; +} + +static int +gbt_bytea_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + return 0; +} + +/* + * We are always aborting the abbreviation. + */ +static bool +gbt_bytea_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return true; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bytea_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_bytea_cmp_abbrev; + ssup->abbrev_converter = gbt_bytea_abbrev_convert; + ssup->abbrev_abort = gbt_bytea_abbrev_abort; + ssup->abbrev_full_comparator = gbt_bytea_cmp_sort; + } + else + { + ssup->comparator = gbt_bytea_cmp_sort; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c index dfa23224b6..558c5eb325 100644 --- a/contrib/btree_gist/btree_cash.c +++ b/contrib/btree_gist/btree_cash.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_cash_consistent); PG_FUNCTION_INFO_V1(gbt_cash_distance); PG_FUNCTION_INFO_V1(gbt_cash_penalty); PG_FUNCTION_INFO_V1(gbt_cash_same); +PG_FUNCTION_INFO_V1(gbt_cash_sortsupport); static bool gbt_cashgt(const void *a, const void *b, FmgrInfo *flinfo) @@ -216,3 +217,77 @@ gbt_cash_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_cash_cmp(Datum a, Datum b, SortSupport ssup) +{ + cashKEY *ia = (cashKEY *) DatumGetPointer(a); + cashKEY *ib = (cashKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_cash_abbrev_convert(Datum original, SortSupport ssup) +{ + cashKEY *b1 = (cashKEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gbt_cash_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_cash_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_cash_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_cash_cmp_abbrev; + ssup->abbrev_converter = gbt_cash_abbrev_convert; + ssup->abbrev_abort = gbt_cash_abbrev_abort; + ssup->abbrev_full_comparator = gbt_cash_cmp; + } + else + { + ssup->comparator = gbt_cash_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c index 455a265a49..bfed3a6220 100644 --- a/contrib/btree_gist/btree_date.c +++ b/contrib/btree_gist/btree_date.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_date_consistent); PG_FUNCTION_INFO_V1(gbt_date_distance); PG_FUNCTION_INFO_V1(gbt_date_penalty); PG_FUNCTION_INFO_V1(gbt_date_same); +PG_FUNCTION_INFO_V1(gbt_date_sortsupport); static bool gbt_dategt(const void *a, const void *b, FmgrInfo *flinfo) @@ -257,3 +258,65 @@ gbt_date_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_date_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + dateKEY *ia = (dateKEY *) PointerGetDatum(a); + dateKEY *ib = (dateKEY *) PointerGetDatum(b); + int res; + + res = DatumGetInt32(DirectFunctionCall2(date_cmp, + DateADTGetDatum(ia->lower), + DateADTGetDatum(ib->lower))); + if (res == 0) + return DatumGetInt32(DirectFunctionCall2(date_cmp, + DateADTGetDatum(ia->upper), + DateADTGetDatum(ib->upper))); + + return res; +} + +static Datum +gbt_date_abbrev_convert(Datum original, SortSupport ssup) +{ + return (Datum) 0; +} + +static int +gbt_date_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + return 0; +} + +/* + * We are always aborting the abbreviation. + */ +static bool +gbt_date_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return true; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_date_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_date_cmp_abbrev; + ssup->abbrev_converter = gbt_date_abbrev_convert; + ssup->abbrev_abort = gbt_date_abbrev_abort; + ssup->abbrev_full_comparator = gbt_date_cmp_sort; + } + else + { + ssup->comparator = gbt_date_cmp_sort; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_enum.c b/contrib/btree_gist/btree_enum.c index d4dc38a38e..404117a201 100644 --- a/contrib/btree_gist/btree_enum.c +++ b/contrib/btree_gist/btree_enum.c @@ -26,6 +26,7 @@ PG_FUNCTION_INFO_V1(gbt_enum_picksplit); PG_FUNCTION_INFO_V1(gbt_enum_consistent); PG_FUNCTION_INFO_V1(gbt_enum_penalty); PG_FUNCTION_INFO_V1(gbt_enum_same); +PG_FUNCTION_INFO_V1(gbt_enum_sortsupport); static bool @@ -183,3 +184,72 @@ gbt_enum_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_enum_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_enum_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + Oid z = b1->lower; + + return (Datum) z; +} + +static int +gbt_enum_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_enum_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_enum_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_enum_cmp_abbrev; + ssup->abbrev_converter = gbt_enum_abbrev_convert; + ssup->abbrev_abort = gbt_enum_abbrev_abort; + ssup->abbrev_full_comparator = gbt_enum_cmp; + } + else + { + ssup->comparator = gbt_enum_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c index 3604c73313..d836d62bb1 100644 --- a/contrib/btree_gist/btree_float4.c +++ b/contrib/btree_gist/btree_float4.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float4_consistent); PG_FUNCTION_INFO_V1(gbt_float4_distance); PG_FUNCTION_INFO_V1(gbt_float4_penalty); PG_FUNCTION_INFO_V1(gbt_float4_same); +PG_FUNCTION_INFO_V1(gbt_float4_sortsupport); static bool gbt_float4gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -209,3 +210,73 @@ gbt_float4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + + +static int +gbt_float4_cmp(Datum a, Datum b, SortSupport ssup) +{ + float4KEY *ia = (float4KEY *) DatumGetPointer(a); + float4KEY *ib = (float4KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float4_abbrev_convert(Datum original, SortSupport ssup) +{ + float4KEY *b1 = (float4KEY *) DatumGetPointer(original); + float4 z = b1->lower; + + return (Datum) Float4GetDatum(z); +} + +static int +gbt_float4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (DatumGetFloat4(z1) > DatumGetFloat4(z2)) + return 1; + else if (DatumGetFloat4(z1) < DatumGetFloat4(z2)) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_float4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float4_cmp_abbrev; + ssup->abbrev_converter = gbt_float4_abbrev_convert; + ssup->abbrev_abort = gbt_float4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float4_cmp; + } + else + { + ssup->comparator = gbt_float4_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c index 10a5262aaa..8eb8a93710 100644 --- a/contrib/btree_gist/btree_float8.c +++ b/contrib/btree_gist/btree_float8.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float8_consistent); PG_FUNCTION_INFO_V1(gbt_float8_distance); PG_FUNCTION_INFO_V1(gbt_float8_penalty); PG_FUNCTION_INFO_V1(gbt_float8_same); +PG_FUNCTION_INFO_V1(gbt_float8_sortsupport); static bool @@ -216,3 +217,81 @@ gbt_float8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_float8_cmp(Datum a, Datum b, SortSupport ssup) +{ + float8KEY *ia = (float8KEY *) DatumGetPointer(a); + float8KEY *ib = (float8KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float8_abbrev_convert(Datum original, SortSupport ssup) +{ + float8KEY *b1 = (float8KEY *) DatumGetPointer(original); + float8 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) Float8GetDatum(z); +#else + return (Datum) 0; +#endif +} + +static int +gbt_float8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + if (DatumGetFloat8(z1) > DatumGetFloat8(z2)) + return 1; + else if (DatumGetFloat8(z1) < DatumGetFloat8(z2)) + return -1; + else + return 0; +#else + return 0; +#endif +} + +static bool +gbt_float8_abbrev_abort(int memtupcount, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + return false; +#else + return true; +#endif +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float8_cmp_abbrev; + ssup->abbrev_converter = gbt_float8_abbrev_convert; + ssup->abbrev_abort = gbt_float8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float8_cmp; + } + else + { + ssup->comparator = gbt_float8_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_gist--1.6--1.7.sql b/contrib/btree_gist/btree_gist--1.6--1.7.sql new file mode 100644 index 0000000000..bd98a4758e --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.6--1.7.sql @@ -0,0 +1,176 @@ +/* contrib/btree_gist/btree_gist--1.6--1.7.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.7'" to load this file. \quit + + +CREATE FUNCTION gbt_int8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD + FUNCTION 11 (int8, int8) gbt_int8_sortsupport (internal) ; + +CREATE FUNCTION gbt_int4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD + FUNCTION 11 (int4, int4) gbt_int4_sortsupport (internal) ; + +CREATE FUNCTION gbt_int2_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD + FUNCTION 11 (int2, int2) gbt_int2_sortsupport (internal) ; + +CREATE FUNCTION gbt_float8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD + FUNCTION 11 (float8, float8) gbt_float8_sortsupport (internal) ; + +CREATE FUNCTION gbt_float4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD + FUNCTION 11 (float4, float4) gbt_float4_sortsupport (internal) ; + +CREATE FUNCTION gbt_enum_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD + FUNCTION 11 (anyenum, anyenum) gbt_enum_sortsupport (internal) ; + +CREATE FUNCTION gbt_oid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD + FUNCTION 11 (oid, oid) gbt_oid_sortsupport (internal) ; + +CREATE FUNCTION gbt_cash_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD + FUNCTION 11 (money, money) gbt_cash_sortsupport (internal) ; + +CREATE FUNCTION gbt_inet_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD + FUNCTION 11 (inet, inet) gbt_inet_sortsupport (internal) ; + + +CREATE FUNCTION gbt_macad_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD + FUNCTION 11 (macaddr, macaddr) gbt_macad_sortsupport (internal) ; + +CREATE FUNCTION gbt_macad8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD + FUNCTION 11 (macaddr8, macaddr8) gbt_macad8_sortsupport (internal) ; + +CREATE FUNCTION gbt_numeric_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD + FUNCTION 11 (numeric, numeric) gbt_numeric_sortsupport (internal) ; + +CREATE FUNCTION gbt_uuid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD + FUNCTION 11 (uuid, uuid) gbt_uuid_sortsupport (internal) ; + +CREATE FUNCTION gbt_ts_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD + FUNCTION 11 (timestamp, timestamp) gbt_ts_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (timestamptz, timestamptz) gbt_ts_sortsupport (internal) ; + +CREATE FUNCTION gbt_text_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_text_ops USING gist ADD + FUNCTION 11 (text, text) gbt_text_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (text, text) gbt_text_sortsupport (internal) ; + +CREATE FUNCTION gbt_time_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_time_ops USING gist ADD + FUNCTION 11 (time, time) gbt_time_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (time, time) gbt_time_sortsupport (internal) ; + +CREATE FUNCTION gbt_bytea_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD + FUNCTION 11 (bytea, bytea) gbt_bytea_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (bytea, bytea) gbt_bytea_sortsupport (internal) ; + +CREATE FUNCTION gbt_date_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_date_ops USING gist ADD + FUNCTION 11 (date, date) gbt_date_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (date, date) gbt_date_sortsupport (internal) ; + +CREATE FUNCTION gbt_bit_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_date_ops USING gist ADD + FUNCTION 11 (bit, bit) gbt_bit_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (bit, bit) gbt_bit_sortsupport (internal) ; diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control index e5c41fe8f3..fa9171a80a 100644 --- a/contrib/btree_gist/btree_gist.control +++ b/contrib/btree_gist/btree_gist.control @@ -1,6 +1,6 @@ # btree_gist extension comment = 'support for indexing common datatypes in GiST' -default_version = '1.6' +default_version = '1.7' module_pathname = '$libdir/btree_gist' relocatable = true trusted = true diff --git a/contrib/btree_gist/btree_gist.h b/contrib/btree_gist/btree_gist.h index 14c7c8ee19..35ad287ed3 100644 --- a/contrib/btree_gist/btree_gist.h +++ b/contrib/btree_gist/btree_gist.h @@ -6,6 +6,7 @@ #include "access/nbtree.h" #include "fmgr.h" +#include "utils/sortsupport.h" #define BtreeGistNotEqualStrategyNumber 6 diff --git a/contrib/btree_gist/btree_inet.c b/contrib/btree_gist/btree_inet.c index e4b3a946b2..61cac1df00 100644 --- a/contrib/btree_gist/btree_inet.c +++ b/contrib/btree_gist/btree_inet.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_inet_picksplit); PG_FUNCTION_INFO_V1(gbt_inet_consistent); PG_FUNCTION_INFO_V1(gbt_inet_penalty); PG_FUNCTION_INFO_V1(gbt_inet_same); +PG_FUNCTION_INFO_V1(gbt_inet_sortsupport); static bool @@ -186,3 +187,82 @@ gbt_inet_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_inet_cmp(Datum a, Datum b, SortSupport ssup) +{ + inetKEY *ia = (inetKEY *) DatumGetPointer(a); + inetKEY *ib = (inetKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_inet_abbrev_convert(Datum original, SortSupport ssup) +{ + inetKEY *b1 = (inetKEY *) DatumGetPointer(original); + double z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) Float8GetDatum(z); +#else + return (Datum) 0; +#endif +} + +static int +gbt_inet_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + if (DatumGetFloat8(z1) > DatumGetFloat8(z2)) + return 1; + else if (DatumGetFloat8(z1) < DatumGetFloat8(z2)) + return -1; + else + return 0; +#else + return 0; +#endif +} + +static bool +gbt_inet_abbrev_abort(int memtupcount, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + return false; +#else + return true; +#endif +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_inet_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_inet_cmp_abbrev; + ssup->abbrev_converter = gbt_inet_abbrev_convert; + ssup->abbrev_abort = gbt_inet_abbrev_abort; + ssup->abbrev_full_comparator = gbt_inet_cmp; + } + else + { + ssup->comparator = gbt_inet_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c index a91b95ff39..abcff85a93 100644 --- a/contrib/btree_gist/btree_int2.c +++ b/contrib/btree_gist/btree_int2.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int2_consistent); PG_FUNCTION_INFO_V1(gbt_int2_distance); PG_FUNCTION_INFO_V1(gbt_int2_penalty); PG_FUNCTION_INFO_V1(gbt_int2_same); +PG_FUNCTION_INFO_V1(gbt_int2_sortsupport); static bool gbt_int2gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -214,3 +215,72 @@ gbt_int2_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int2_cmp(Datum a, Datum b, SortSupport ssup) +{ + int16KEY *ia = (int16KEY *) DatumGetPointer(a); + int16KEY *ib = (int16KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int2_abbrev_convert(Datum original, SortSupport ssup) +{ + int16KEY *b1 = (int16KEY *) DatumGetPointer(original); + int16 z = b1->lower; + + return (Datum) z; +} + +static int +gbt_int2_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int2_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int2_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int2_cmp_abbrev; + ssup->abbrev_converter = gbt_int2_abbrev_convert; + ssup->abbrev_abort = gbt_int2_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int2_cmp; + } + else + { + ssup->comparator = gbt_int2_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c index 7ea98c478c..327d771230 100644 --- a/contrib/btree_gist/btree_int4.c +++ b/contrib/btree_gist/btree_int4.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int4_consistent); PG_FUNCTION_INFO_V1(gbt_int4_distance); PG_FUNCTION_INFO_V1(gbt_int4_penalty); PG_FUNCTION_INFO_V1(gbt_int4_same); +PG_FUNCTION_INFO_V1(gbt_int4_sortsupport); static bool @@ -215,3 +216,72 @@ gbt_int4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int4_cmp(Datum a, Datum b, SortSupport ssup) +{ + int32KEY *ia = (int32KEY *) DatumGetPointer(a); + int32KEY *ib = (int32KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int4_abbrev_convert(Datum original, SortSupport ssup) +{ + int32KEY *b1 = (int32KEY *) DatumGetPointer(original); + int32 z = b1->lower; + + return (Datum) z; +} + +static int +gbt_int4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int4_cmp_abbrev; + ssup->abbrev_converter = gbt_int4_abbrev_convert; + ssup->abbrev_abort = gbt_int4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int4_cmp; + } + else + { + ssup->comparator = gbt_int4_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c index df2b0d174b..32f8c3b8b4 100644 --- a/contrib/btree_gist/btree_int8.c +++ b/contrib/btree_gist/btree_int8.c @@ -24,7 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int8_consistent); PG_FUNCTION_INFO_V1(gbt_int8_distance); PG_FUNCTION_INFO_V1(gbt_int8_penalty); PG_FUNCTION_INFO_V1(gbt_int8_same); - +PG_FUNCTION_INFO_V1(gbt_int8_sortsupport); static bool gbt_int8gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -215,3 +215,76 @@ gbt_int8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int8_cmp(Datum a, Datum b, SortSupport ssup) +{ + int64KEY *ia = (int64KEY *) DatumGetPointer(a); + int64KEY *ib = (int64KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int8_abbrev_convert(Datum original, SortSupport ssup) +{ + int64KEY *b1 = (int64KEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gbt_int8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int8_cmp_abbrev; + ssup->abbrev_converter = gbt_int8_abbrev_convert; + ssup->abbrev_abort = gbt_int8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int8_cmp; + } + else + { + ssup->comparator = gbt_int8_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_macaddr.c b/contrib/btree_gist/btree_macaddr.c index 7f0e9e9c91..bda5de3cfc 100644 --- a/contrib/btree_gist/btree_macaddr.c +++ b/contrib/btree_gist/btree_macaddr.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad_picksplit); PG_FUNCTION_INFO_V1(gbt_macad_consistent); PG_FUNCTION_INFO_V1(gbt_macad_penalty); PG_FUNCTION_INFO_V1(gbt_macad_same); +PG_FUNCTION_INFO_V1(gbt_macad_sortsupport); static bool @@ -195,3 +196,65 @@ gbt_macad_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad_cmp(Datum a, Datum b, SortSupport ssup) +{ + macKEY *ma = (macKEY *) DatumGetPointer(a); + macKEY *mb = (macKEY *) DatumGetPointer(b); + uint64 ia = mac_2_uint64(&ma->lower); + uint64 ib = mac_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + { + return 0; + } + + return (ia > ib) ? 1 : -1; +} + +static Datum +gbt_macad_abbrev_convert(Datum original, SortSupport ssup) +{ + return (Datum) 0; +} + +static int +gbt_macad_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + return 0; +} + +/* + * We are always aborting the abbreviation. + */ +static bool +gbt_macad_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return true; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad_cmp_abbrev; + ssup->abbrev_converter = gbt_macad_abbrev_convert; + ssup->abbrev_abort = gbt_macad_abbrev_abort; + ssup->abbrev_full_comparator = gbt_macad_cmp; + } + else + { + ssup->comparator = gbt_macad_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_macaddr8.c b/contrib/btree_gist/btree_macaddr8.c index ab4bca5d50..4980b5df6e 100644 --- a/contrib/btree_gist/btree_macaddr8.c +++ b/contrib/btree_gist/btree_macaddr8.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad8_picksplit); PG_FUNCTION_INFO_V1(gbt_macad8_consistent); PG_FUNCTION_INFO_V1(gbt_macad8_penalty); PG_FUNCTION_INFO_V1(gbt_macad8_same); +PG_FUNCTION_INFO_V1(gbt_macad8_sortsupport); static bool @@ -195,3 +196,64 @@ gbt_macad8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad8_cmp(Datum a, Datum b, SortSupport ssup) +{ + mac8KEY *ma = (mac8KEY *) DatumGetPointer(a); + mac8KEY *mb = (mac8KEY *) DatumGetPointer(b); + uint64 ia = mac8_2_uint64(&ma->lower); + uint64 ib = mac8_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + { + return 0; + } + + return (ia > ib) ? 1 : -1; +} + +static Datum +gbt_macad8_abbrev_convert(Datum original, SortSupport ssup) +{ + return (Datum) 0; +} + +static int +gbt_macad8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + return 0; +} + +/* + * We are always aborting the abbreviation. + */ +static bool +gbt_macad8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return true; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad8_cmp_abbrev; + ssup->abbrev_converter = gbt_macad8_abbrev_convert; + ssup->abbrev_abort = gbt_macad8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_macad8_cmp; + } + else + { + ssup->comparator = gbt_macad8_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_numeric.c b/contrib/btree_gist/btree_numeric.c index 35e466cdd9..e1a29402f3 100644 --- a/contrib/btree_gist/btree_numeric.c +++ b/contrib/btree_gist/btree_numeric.c @@ -21,6 +21,7 @@ PG_FUNCTION_INFO_V1(gbt_numeric_picksplit); PG_FUNCTION_INFO_V1(gbt_numeric_consistent); PG_FUNCTION_INFO_V1(gbt_numeric_penalty); PG_FUNCTION_INFO_V1(gbt_numeric_same); +PG_FUNCTION_INFO_V1(gbt_numeric_sortsupport); /* define for comparison */ @@ -227,3 +228,54 @@ gbt_numeric_picksplit(PG_FUNCTION_ARGS) &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(v); } + +static int +gbt_numeric_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(numeric_cmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +static Datum +gbt_numeric_abbrev_convert(Datum original, SortSupport ssup) +{ + return (Datum) 0; +} + +static int +gbt_numeric_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + return 0; +} + +/* + * We are always aborting the abbreviation. + */ +static bool +gbt_numeric_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return true; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_numeric_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_numeric_cmp_abbrev; + ssup->abbrev_converter = gbt_numeric_abbrev_convert; + ssup->abbrev_abort = gbt_numeric_abbrev_abort; + ssup->abbrev_full_comparator = gbt_numeric_cmp_sort; + } + else + { + ssup->comparator = gbt_numeric_cmp_sort; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c index 3cc7d4245d..5dcfc2ec48 100644 --- a/contrib/btree_gist/btree_oid.c +++ b/contrib/btree_gist/btree_oid.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_oid_consistent); PG_FUNCTION_INFO_V1(gbt_oid_distance); PG_FUNCTION_INFO_V1(gbt_oid_penalty); PG_FUNCTION_INFO_V1(gbt_oid_same); +PG_FUNCTION_INFO_V1(gbt_oid_sortsupport); static bool @@ -215,3 +216,73 @@ gbt_oid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_oid_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_oid_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + Oid z = b1->lower; + + return (Datum) z; +} + +static int +gbt_oid_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_oid_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_oid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_oid_cmp_abbrev; + ssup->abbrev_converter = gbt_oid_abbrev_convert; + ssup->abbrev_abort = gbt_oid_abbrev_abort; + ssup->abbrev_full_comparator = gbt_oid_cmp; + } + else + { + ssup->comparator = gbt_oid_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 8019d11281..8ee866ee80 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_text_consistent); PG_FUNCTION_INFO_V1(gbt_bpchar_consistent); PG_FUNCTION_INFO_V1(gbt_text_penalty); PG_FUNCTION_INFO_V1(gbt_text_same); +PG_FUNCTION_INFO_V1(gbt_text_sortsupport); /* define for comparison */ @@ -239,3 +240,56 @@ gbt_text_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_text_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2Coll(bttextcmp, + ssup->ssup_collation, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +static Datum +gbt_text_abbrev_convert(Datum original, SortSupport ssup) +{ + return (Datum) 0; +} + +static int +gbt_text_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + return 0; +} + +/* + * We are always aborting the abbreviation. + */ +static bool +gbt_text_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return true; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_text_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_text_cmp_abbrev; + ssup->abbrev_converter = gbt_text_abbrev_convert; + ssup->abbrev_abort = gbt_text_abbrev_abort; + ssup->abbrev_full_comparator = gbt_text_cmp_sort; + } + else + { + ssup->comparator = gbt_text_cmp_sort; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c index fd8774a2f0..b2d05bdc56 100644 --- a/contrib/btree_gist/btree_time.c +++ b/contrib/btree_gist/btree_time.c @@ -28,6 +28,7 @@ PG_FUNCTION_INFO_V1(gbt_time_distance); PG_FUNCTION_INFO_V1(gbt_timetz_consistent); PG_FUNCTION_INFO_V1(gbt_time_penalty); PG_FUNCTION_INFO_V1(gbt_time_same); +PG_FUNCTION_INFO_V1(gbt_time_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -332,3 +333,62 @@ gbt_time_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_time_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + timeKEY *ia = (timeKEY *) DatumGetPointer(a); + timeKEY *ib = (timeKEY *) DatumGetPointer(b); + int res; + + res = DatumGetInt32(DirectFunctionCall2(time_cmp, TimeADTGetDatumFast(ia->lower), TimeADTGetDatumFast(ib->lower))); + if (res == 0) + return DatumGetInt32(DirectFunctionCall2(time_cmp, TimeADTGetDatumFast(ia->upper), TimeADTGetDatumFast(ib->upper))); + + return res; +} + +static Datum +gbt_time_abbrev_convert(Datum original, SortSupport ssup) +{ + return (Datum) 0; +} + +static int +gbt_time_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + return 0; +} + +/* + * We are always aborting the abbreviation. + */ +static bool +gbt_time_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return true; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_time_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_time_cmp_abbrev; + ssup->abbrev_converter = gbt_time_abbrev_convert; + ssup->abbrev_abort = gbt_time_abbrev_abort; + ssup->abbrev_full_comparator = gbt_time_cmp_sort; + } + else + { + ssup->comparator = gbt_time_cmp_sort; + } + PG_RETURN_VOID(); +} + + diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c index 2671ba961c..35548fc49c 100644 --- a/contrib/btree_gist/btree_ts.c +++ b/contrib/btree_gist/btree_ts.c @@ -31,6 +31,7 @@ PG_FUNCTION_INFO_V1(gbt_tstz_consistent); PG_FUNCTION_INFO_V1(gbt_tstz_distance); PG_FUNCTION_INFO_V1(gbt_ts_penalty); PG_FUNCTION_INFO_V1(gbt_ts_same); +PG_FUNCTION_INFO_V1(gbt_ts_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -399,3 +400,61 @@ gbt_ts_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_ts_cmp(Datum a, Datum b, SortSupport ssup) +{ + tsKEY *ia = (tsKEY *) DatumGetPointer(a); + tsKEY *ib = (tsKEY *) DatumGetPointer(b); + int res; + + res = DatumGetInt32(DirectFunctionCall2(timestamp_cmp, TimestampGetDatumFast(ia->lower), TimestampGetDatumFast(ib->lower))); + if (res == 0) + return DatumGetInt32(DirectFunctionCall2(timestamp_cmp, TimestampGetDatumFast(ia->upper), TimestampGetDatumFast(ib->upper))); + + return res; +} + +static Datum +gbt_ts_abbrev_convert(Datum original, SortSupport ssup) +{ + return (Datum) 0; +} + +static int +gbt_ts_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + return 0; +} + +/* + * We are always aborting the abbreviation. + */ +static bool +gbt_ts_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return true; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_ts_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_ts_cmp_abbrev; + ssup->abbrev_converter = gbt_ts_abbrev_convert; + ssup->abbrev_abort = gbt_ts_abbrev_abort; + ssup->abbrev_full_comparator = gbt_ts_cmp; + } + else + { + ssup->comparator = gbt_ts_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_uuid.c b/contrib/btree_gist/btree_uuid.c index b81875979a..2beffcb2e6 100644 --- a/contrib/btree_gist/btree_uuid.c +++ b/contrib/btree_gist/btree_uuid.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_uuid_picksplit); PG_FUNCTION_INFO_V1(gbt_uuid_consistent); PG_FUNCTION_INFO_V1(gbt_uuid_penalty); PG_FUNCTION_INFO_V1(gbt_uuid_same); +PG_FUNCTION_INFO_V1(gbt_uuid_sortsupport); static int @@ -233,3 +234,56 @@ gbt_uuid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_uuid_cmp(Datum a, Datum b, SortSupport ssup) +{ + uuidKEY *ua = (uuidKEY *) DatumGetPointer(a); + uuidKEY *ub = (uuidKEY *) DatumGetPointer(b); + + return uuid_internal_cmp(&ua->lower, &ub->lower); +} + +static Datum +gbt_uuid_abbrev_convert(Datum original, SortSupport ssup) +{ + return (Datum) 0; +} + +static int +gbt_uuid_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + return 0; +} + +/* + * We are always aborting the abbreviation. + */ +static bool +gbt_uuid_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return true; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_uuid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_uuid_cmp_abbrev; + ssup->abbrev_converter = gbt_uuid_abbrev_convert; + ssup->abbrev_abort = gbt_uuid_abbrev_abort; + ssup->abbrev_full_comparator = gbt_uuid_cmp; + } + else + { + ssup->comparator = gbt_uuid_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index 8a14620fab..e600015b12 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -338,6 +338,7 @@ gistadjustmembers(Oid opfamilyoid, case GIST_DISTANCE_PROC: case GIST_FETCH_PROC: case GIST_OPTIONS_PROC: + case GIST_SORTSUPPORT_PROC: /* Optional, so force it to be a soft family dependency */ op->ref_is_hard = false; op->ref_is_family = true; -- 2.24.3 (Apple Git-128) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-10-21 17:35  Pavel Borisov <pashkin.elfe@gmail.com>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  1 sibling, 0 replies; 111+ messages in thread

From: Pavel Borisov @ 2020-10-21 17:35 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

>
>
> >> +    return (ia->lower > ib->lower) ? 1 : -1;
> >> +}
> >
> > We're only dealing with leaf items during index build, so the 'upper'
> and 'lower' should always be equal here, right? Maybe add a comment and an
> assertion on that.
> >
> > (It's pretty sad that the on-disk representation is identical for leaf
> and internal items, because that wastes a lot of disk space, but that's way
> out of scope for this patch.)
>
> Thanks, I've added assert() where is was easy to test equalty.
>
> PFA patch with all types.
>
> I had a plan to implement and test one type each day. I did not quite
> understood how rich our type system is.
>

Cool, thanks!

BTW there is a somewhat parallel discussion on this gist patch in
pgsql-bugs which you may miss
https://www.postgresql.org/message-id/flat/3dda6945-c147-5afc-7720-38ec6848dafa%40gmail.com#9be5b8a7...

The main point is that buffering build is better to be enforced with
buffering=on
as otherwise buffering build becomes hard to test in the presence of sort
support.

-- 
Best regards,
Pavel Borisov

Postgres Professional: http://postgrespro.com <http://www.postgrespro.com;

^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-10-27 11:43  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  1 sibling, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2020-10-27 11:43 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 21/10/2020 20:13, Andrey Borodin wrote:
>> 7 окт. 2020 г., в 17:38, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
>>
>> On 07/10/2020 15:27, Andrey Borodin wrote:
>>> Here's draft patch with implementation of sortsupport for ints and floats.
>>
>>> +static int
>>> +gbt_int4_cmp(Datum a, Datum b, SortSupport ssup)
>>> +{
>>> +	int32KEY   *ia = (int32KEY *) DatumGetPointer(a);
>>> +	int32KEY   *ib = (int32KEY *) DatumGetPointer(b);
>>> +
>>> +	if (ia->lower == ib->lower)
>>> +	{
>>> +		if (ia->upper == ib->upper)
>>> +			return 0;
>>> +
>>> +		return (ia->upper > ib->upper) ? 1 : -1;
>>> +	}
>>> +
>>> +	return (ia->lower > ib->lower) ? 1 : -1;
>>> +}
>>
>> We're only dealing with leaf items during index build, so the 'upper' and 'lower' should always be equal here, right? Maybe add a comment and an assertion on that.
>>
>> (It's pretty sad that the on-disk representation is identical for leaf and internal items, because that wastes a lot of disk space, but that's way out of scope for this patch.)
> 
> Thanks, I've added assert() where is was easy to test equalty.
> 
> PFA patch with all types.

gbt_ts_cmp(), gbt_time_cmp_sort() and gbt_date_cmp_sort() still have the 
above issue, they still compare "upper" for no good reason.

> +static Datum
> +gbt_bit_abbrev_convert(Datum original, SortSupport ssup)
> +{
> +       return (Datum) 0;
> +}
> +
> +static int
> +gbt_bit_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup)
> +{
> +       return 0;
> +}

If an abbreviated key is not useful, just don't define abbrev functions 
and don't set SortSupport->abbrev_converter in the first place.

> static bool
> gbt_inet_abbrev_abort(int memtupcount, SortSupport ssup)
> {
> #if SIZEOF_DATUM == 8
> 	return false;
> #else
> 	return true;
> #endif
> }

Better to not set the 'abbrev_converter' function in the first place. Or 
would it be better to cast the float8 to float4 if SIZEOF_DATUM == 4?

> I had a plan to implement and test one type each day. I did not quite understood how rich our type system is.

:-)

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-10-30 18:20  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2020-10-30 18:20 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

Thanks!

> 27 окт. 2020 г., в 16:43, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> gbt_ts_cmp(), gbt_time_cmp_sort() and gbt_date_cmp_sort() still have the above issue, they still compare "upper" for no good reason.
Fixed.

>> +static Datum
>> +gbt_bit_abbrev_convert(Datum original, SortSupport ssup)
>> +{
>> +       return (Datum) 0;
>> +}
>> +
>> +static int
>> +gbt_bit_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup)
>> +{
>> +       return 0;
>> +}
> 
> If an abbreviated key is not useful, just don't define abbrev functions and don't set SortSupport->abbrev_converter in the first place.
Fixed.
> 
>> static bool
>> gbt_inet_abbrev_abort(int memtupcount, SortSupport ssup)
>> {
>> #if SIZEOF_DATUM == 8
>> 	return false;
>> #else
>> 	return true;
>> #endif
>> }
> 
> Better to not set the 'abbrev_converter' function in the first place. Or would it be better to cast the float8 to float4 if SIZEOF_DATUM == 4?
Ok, now for 4 bytes Datum we do return (Datum) Float4GetDatum((float) z);

How do you think, should this patch and patch with pageinspect GiST functions be registered on commitfest?

Thanks!

Best regards, Andrey Borodin.

Attachments:

v3-0001-Sortsupport-for-sorting-GiST-build-for-gist_btree.patch (application/octet-stream, 40.4K) download From d72845dab91e3f1aa9e8f751ce994c8eec7250ff Mon Sep 17 00:00:00 2001 From: Andrey Borodin <amborodin@acm.org> Date: Sat, 3 Oct 2020 21:08:54 +0500 Subject: [PATCH v3] Sortsupport for sorting GiST build for gist_btree types --- contrib/btree_gist/Makefile | 2 +- contrib/btree_gist/btree_bit.c | 32 ++++ contrib/btree_gist/btree_bytea.c | 33 +++- contrib/btree_gist/btree_cash.c | 75 +++++++++ contrib/btree_gist/btree_date.c | 35 ++++ contrib/btree_gist/btree_enum.c | 70 ++++++++ contrib/btree_gist/btree_float4.c | 71 ++++++++ contrib/btree_gist/btree_float8.c | 79 +++++++++ contrib/btree_gist/btree_gist--1.6--1.7.sql | 176 ++++++++++++++++++++ contrib/btree_gist/btree_gist.control | 2 +- contrib/btree_gist/btree_gist.h | 1 + contrib/btree_gist/btree_inet.c | 81 +++++++++ contrib/btree_gist/btree_int2.c | 70 ++++++++ contrib/btree_gist/btree_int4.c | 70 ++++++++ contrib/btree_gist/btree_int8.c | 75 ++++++++- contrib/btree_gist/btree_macaddr.c | 42 +++++ contrib/btree_gist/btree_macaddr8.c | 41 +++++ contrib/btree_gist/btree_numeric.c | 31 ++++ contrib/btree_gist/btree_oid.c | 71 ++++++++ contrib/btree_gist/btree_text.c | 33 ++++ contrib/btree_gist/btree_time.c | 34 ++++ contrib/btree_gist/btree_ts.c | 33 ++++ contrib/btree_gist/btree_uuid.c | 33 ++++ src/backend/access/gist/gistvalidate.c | 1 + 24 files changed, 1187 insertions(+), 4 deletions(-) create mode 100644 contrib/btree_gist/btree_gist--1.6--1.7.sql diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile index e92d974a1a..a1f818f71e 100644 --- a/contrib/btree_gist/Makefile +++ b/contrib/btree_gist/Makefile @@ -32,7 +32,7 @@ EXTENSION = btree_gist DATA = btree_gist--1.0--1.1.sql \ btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \ - btree_gist--1.5--1.6.sql + btree_gist--1.5--1.6.sql btree_gist--1.6--1.7.sql PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes" REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \ diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c index 2225244ded..8885914035 100644 --- a/contrib/btree_gist/btree_bit.c +++ b/contrib/btree_gist/btree_bit.c @@ -19,6 +19,7 @@ PG_FUNCTION_INFO_V1(gbt_bit_picksplit); PG_FUNCTION_INFO_V1(gbt_bit_consistent); PG_FUNCTION_INFO_V1(gbt_bit_penalty); PG_FUNCTION_INFO_V1(gbt_bit_same); +PG_FUNCTION_INFO_V1(gbt_bit_sortsupport); /* define for comparison */ @@ -209,3 +210,34 @@ gbt_bit_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bit_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bit_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_bit_cmp_sort; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + } + else + { + ssup->comparator = gbt_bit_cmp_sort; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_bytea.c b/contrib/btree_gist/btree_bytea.c index 6b005f0157..9e3752e90c 100644 --- a/contrib/btree_gist/btree_bytea.c +++ b/contrib/btree_gist/btree_bytea.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_bytea_picksplit); PG_FUNCTION_INFO_V1(gbt_bytea_consistent); PG_FUNCTION_INFO_V1(gbt_bytea_penalty); PG_FUNCTION_INFO_V1(gbt_bytea_same); +PG_FUNCTION_INFO_V1(gbt_bytea_sortsupport); /* define for comparison */ @@ -87,7 +88,7 @@ static const gbtree_vinfo tinfo = /************************************************** - * Text ops + * Bytea ops **************************************************/ @@ -168,3 +169,33 @@ gbt_bytea_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bytea_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bytea_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_bytea_cmp_sort; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + } + else + { + ssup->comparator = gbt_bytea_cmp_sort; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c index dfa23224b6..558c5eb325 100644 --- a/contrib/btree_gist/btree_cash.c +++ b/contrib/btree_gist/btree_cash.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_cash_consistent); PG_FUNCTION_INFO_V1(gbt_cash_distance); PG_FUNCTION_INFO_V1(gbt_cash_penalty); PG_FUNCTION_INFO_V1(gbt_cash_same); +PG_FUNCTION_INFO_V1(gbt_cash_sortsupport); static bool gbt_cashgt(const void *a, const void *b, FmgrInfo *flinfo) @@ -216,3 +217,77 @@ gbt_cash_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_cash_cmp(Datum a, Datum b, SortSupport ssup) +{ + cashKEY *ia = (cashKEY *) DatumGetPointer(a); + cashKEY *ib = (cashKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_cash_abbrev_convert(Datum original, SortSupport ssup) +{ + cashKEY *b1 = (cashKEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gbt_cash_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_cash_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_cash_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_cash_cmp_abbrev; + ssup->abbrev_converter = gbt_cash_abbrev_convert; + ssup->abbrev_abort = gbt_cash_abbrev_abort; + ssup->abbrev_full_comparator = gbt_cash_cmp; + } + else + { + ssup->comparator = gbt_cash_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c index 455a265a49..7e55d6b997 100644 --- a/contrib/btree_gist/btree_date.c +++ b/contrib/btree_gist/btree_date.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_date_consistent); PG_FUNCTION_INFO_V1(gbt_date_distance); PG_FUNCTION_INFO_V1(gbt_date_penalty); PG_FUNCTION_INFO_V1(gbt_date_same); +PG_FUNCTION_INFO_V1(gbt_date_sortsupport); static bool gbt_dategt(const void *a, const void *b, FmgrInfo *flinfo) @@ -257,3 +258,37 @@ gbt_date_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_date_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + dateKEY *ia = (dateKEY *) PointerGetDatum(a); + dateKEY *ib = (dateKEY *) PointerGetDatum(b); + + return DatumGetInt32(DirectFunctionCall2(date_cmp, + DateADTGetDatum(ia->lower), + DateADTGetDatum(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_date_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_date_cmp_sort; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + } + else + { + ssup->comparator = gbt_date_cmp_sort; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_enum.c b/contrib/btree_gist/btree_enum.c index d4dc38a38e..404117a201 100644 --- a/contrib/btree_gist/btree_enum.c +++ b/contrib/btree_gist/btree_enum.c @@ -26,6 +26,7 @@ PG_FUNCTION_INFO_V1(gbt_enum_picksplit); PG_FUNCTION_INFO_V1(gbt_enum_consistent); PG_FUNCTION_INFO_V1(gbt_enum_penalty); PG_FUNCTION_INFO_V1(gbt_enum_same); +PG_FUNCTION_INFO_V1(gbt_enum_sortsupport); static bool @@ -183,3 +184,72 @@ gbt_enum_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_enum_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_enum_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + Oid z = b1->lower; + + return (Datum) z; +} + +static int +gbt_enum_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_enum_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_enum_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_enum_cmp_abbrev; + ssup->abbrev_converter = gbt_enum_abbrev_convert; + ssup->abbrev_abort = gbt_enum_abbrev_abort; + ssup->abbrev_full_comparator = gbt_enum_cmp; + } + else + { + ssup->comparator = gbt_enum_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c index 3604c73313..d836d62bb1 100644 --- a/contrib/btree_gist/btree_float4.c +++ b/contrib/btree_gist/btree_float4.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float4_consistent); PG_FUNCTION_INFO_V1(gbt_float4_distance); PG_FUNCTION_INFO_V1(gbt_float4_penalty); PG_FUNCTION_INFO_V1(gbt_float4_same); +PG_FUNCTION_INFO_V1(gbt_float4_sortsupport); static bool gbt_float4gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -209,3 +210,73 @@ gbt_float4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + + +static int +gbt_float4_cmp(Datum a, Datum b, SortSupport ssup) +{ + float4KEY *ia = (float4KEY *) DatumGetPointer(a); + float4KEY *ib = (float4KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float4_abbrev_convert(Datum original, SortSupport ssup) +{ + float4KEY *b1 = (float4KEY *) DatumGetPointer(original); + float4 z = b1->lower; + + return (Datum) Float4GetDatum(z); +} + +static int +gbt_float4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (DatumGetFloat4(z1) > DatumGetFloat4(z2)) + return 1; + else if (DatumGetFloat4(z1) < DatumGetFloat4(z2)) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_float4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float4_cmp_abbrev; + ssup->abbrev_converter = gbt_float4_abbrev_convert; + ssup->abbrev_abort = gbt_float4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float4_cmp; + } + else + { + ssup->comparator = gbt_float4_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c index 10a5262aaa..8eb8a93710 100644 --- a/contrib/btree_gist/btree_float8.c +++ b/contrib/btree_gist/btree_float8.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float8_consistent); PG_FUNCTION_INFO_V1(gbt_float8_distance); PG_FUNCTION_INFO_V1(gbt_float8_penalty); PG_FUNCTION_INFO_V1(gbt_float8_same); +PG_FUNCTION_INFO_V1(gbt_float8_sortsupport); static bool @@ -216,3 +217,81 @@ gbt_float8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_float8_cmp(Datum a, Datum b, SortSupport ssup) +{ + float8KEY *ia = (float8KEY *) DatumGetPointer(a); + float8KEY *ib = (float8KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float8_abbrev_convert(Datum original, SortSupport ssup) +{ + float8KEY *b1 = (float8KEY *) DatumGetPointer(original); + float8 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) Float8GetDatum(z); +#else + return (Datum) 0; +#endif +} + +static int +gbt_float8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + if (DatumGetFloat8(z1) > DatumGetFloat8(z2)) + return 1; + else if (DatumGetFloat8(z1) < DatumGetFloat8(z2)) + return -1; + else + return 0; +#else + return 0; +#endif +} + +static bool +gbt_float8_abbrev_abort(int memtupcount, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + return false; +#else + return true; +#endif +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float8_cmp_abbrev; + ssup->abbrev_converter = gbt_float8_abbrev_convert; + ssup->abbrev_abort = gbt_float8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float8_cmp; + } + else + { + ssup->comparator = gbt_float8_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_gist--1.6--1.7.sql b/contrib/btree_gist/btree_gist--1.6--1.7.sql new file mode 100644 index 0000000000..bd98a4758e --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.6--1.7.sql @@ -0,0 +1,176 @@ +/* contrib/btree_gist/btree_gist--1.6--1.7.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.7'" to load this file. \quit + + +CREATE FUNCTION gbt_int8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD + FUNCTION 11 (int8, int8) gbt_int8_sortsupport (internal) ; + +CREATE FUNCTION gbt_int4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD + FUNCTION 11 (int4, int4) gbt_int4_sortsupport (internal) ; + +CREATE FUNCTION gbt_int2_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD + FUNCTION 11 (int2, int2) gbt_int2_sortsupport (internal) ; + +CREATE FUNCTION gbt_float8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD + FUNCTION 11 (float8, float8) gbt_float8_sortsupport (internal) ; + +CREATE FUNCTION gbt_float4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD + FUNCTION 11 (float4, float4) gbt_float4_sortsupport (internal) ; + +CREATE FUNCTION gbt_enum_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD + FUNCTION 11 (anyenum, anyenum) gbt_enum_sortsupport (internal) ; + +CREATE FUNCTION gbt_oid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD + FUNCTION 11 (oid, oid) gbt_oid_sortsupport (internal) ; + +CREATE FUNCTION gbt_cash_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD + FUNCTION 11 (money, money) gbt_cash_sortsupport (internal) ; + +CREATE FUNCTION gbt_inet_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD + FUNCTION 11 (inet, inet) gbt_inet_sortsupport (internal) ; + + +CREATE FUNCTION gbt_macad_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD + FUNCTION 11 (macaddr, macaddr) gbt_macad_sortsupport (internal) ; + +CREATE FUNCTION gbt_macad8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD + FUNCTION 11 (macaddr8, macaddr8) gbt_macad8_sortsupport (internal) ; + +CREATE FUNCTION gbt_numeric_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD + FUNCTION 11 (numeric, numeric) gbt_numeric_sortsupport (internal) ; + +CREATE FUNCTION gbt_uuid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD + FUNCTION 11 (uuid, uuid) gbt_uuid_sortsupport (internal) ; + +CREATE FUNCTION gbt_ts_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD + FUNCTION 11 (timestamp, timestamp) gbt_ts_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (timestamptz, timestamptz) gbt_ts_sortsupport (internal) ; + +CREATE FUNCTION gbt_text_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_text_ops USING gist ADD + FUNCTION 11 (text, text) gbt_text_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (text, text) gbt_text_sortsupport (internal) ; + +CREATE FUNCTION gbt_time_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_time_ops USING gist ADD + FUNCTION 11 (time, time) gbt_time_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (time, time) gbt_time_sortsupport (internal) ; + +CREATE FUNCTION gbt_bytea_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD + FUNCTION 11 (bytea, bytea) gbt_bytea_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (bytea, bytea) gbt_bytea_sortsupport (internal) ; + +CREATE FUNCTION gbt_date_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_date_ops USING gist ADD + FUNCTION 11 (date, date) gbt_date_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (date, date) gbt_date_sortsupport (internal) ; + +CREATE FUNCTION gbt_bit_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_date_ops USING gist ADD + FUNCTION 11 (bit, bit) gbt_bit_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (bit, bit) gbt_bit_sortsupport (internal) ; diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control index e5c41fe8f3..fa9171a80a 100644 --- a/contrib/btree_gist/btree_gist.control +++ b/contrib/btree_gist/btree_gist.control @@ -1,6 +1,6 @@ # btree_gist extension comment = 'support for indexing common datatypes in GiST' -default_version = '1.6' +default_version = '1.7' module_pathname = '$libdir/btree_gist' relocatable = true trusted = true diff --git a/contrib/btree_gist/btree_gist.h b/contrib/btree_gist/btree_gist.h index 14c7c8ee19..35ad287ed3 100644 --- a/contrib/btree_gist/btree_gist.h +++ b/contrib/btree_gist/btree_gist.h @@ -6,6 +6,7 @@ #include "access/nbtree.h" #include "fmgr.h" +#include "utils/sortsupport.h" #define BtreeGistNotEqualStrategyNumber 6 diff --git a/contrib/btree_gist/btree_inet.c b/contrib/btree_gist/btree_inet.c index e4b3a946b2..31635fc40c 100644 --- a/contrib/btree_gist/btree_inet.c +++ b/contrib/btree_gist/btree_inet.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_inet_picksplit); PG_FUNCTION_INFO_V1(gbt_inet_consistent); PG_FUNCTION_INFO_V1(gbt_inet_penalty); PG_FUNCTION_INFO_V1(gbt_inet_same); +PG_FUNCTION_INFO_V1(gbt_inet_sortsupport); static bool @@ -186,3 +187,83 @@ gbt_inet_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_inet_cmp(Datum a, Datum b, SortSupport ssup) +{ + inetKEY *ia = (inetKEY *) DatumGetPointer(a); + inetKEY *ib = (inetKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_inet_abbrev_convert(Datum original, SortSupport ssup) +{ + inetKEY *b1 = (inetKEY *) DatumGetPointer(original); + double z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) Float8GetDatum(z); +#else + return (Datum) Float4GetDatum((float) z); +#endif +} + +static int +gbt_inet_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + if (DatumGetFloat8(z1) > DatumGetFloat8(z2)) + return 1; + else if (DatumGetFloat8(z1) < DatumGetFloat8(z2)) + return -1; + else + return 0; +#else + if (DatumGetFloat4(z1) > DatumGetFloat4(z2)) + return 1; + else if (DatumGetFloat4(z1) < DatumGetFloat4(z2)) + return -1; + else + return 0; +#endif +} + +static bool +gbt_inet_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_inet_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_inet_cmp_abbrev; + ssup->abbrev_converter = gbt_inet_abbrev_convert; + ssup->abbrev_abort = gbt_inet_abbrev_abort; + ssup->abbrev_full_comparator = gbt_inet_cmp; + } + else + { + ssup->comparator = gbt_inet_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c index a91b95ff39..abcff85a93 100644 --- a/contrib/btree_gist/btree_int2.c +++ b/contrib/btree_gist/btree_int2.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int2_consistent); PG_FUNCTION_INFO_V1(gbt_int2_distance); PG_FUNCTION_INFO_V1(gbt_int2_penalty); PG_FUNCTION_INFO_V1(gbt_int2_same); +PG_FUNCTION_INFO_V1(gbt_int2_sortsupport); static bool gbt_int2gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -214,3 +215,72 @@ gbt_int2_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int2_cmp(Datum a, Datum b, SortSupport ssup) +{ + int16KEY *ia = (int16KEY *) DatumGetPointer(a); + int16KEY *ib = (int16KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int2_abbrev_convert(Datum original, SortSupport ssup) +{ + int16KEY *b1 = (int16KEY *) DatumGetPointer(original); + int16 z = b1->lower; + + return (Datum) z; +} + +static int +gbt_int2_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int2_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int2_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int2_cmp_abbrev; + ssup->abbrev_converter = gbt_int2_abbrev_convert; + ssup->abbrev_abort = gbt_int2_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int2_cmp; + } + else + { + ssup->comparator = gbt_int2_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c index 7ea98c478c..327d771230 100644 --- a/contrib/btree_gist/btree_int4.c +++ b/contrib/btree_gist/btree_int4.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int4_consistent); PG_FUNCTION_INFO_V1(gbt_int4_distance); PG_FUNCTION_INFO_V1(gbt_int4_penalty); PG_FUNCTION_INFO_V1(gbt_int4_same); +PG_FUNCTION_INFO_V1(gbt_int4_sortsupport); static bool @@ -215,3 +216,72 @@ gbt_int4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int4_cmp(Datum a, Datum b, SortSupport ssup) +{ + int32KEY *ia = (int32KEY *) DatumGetPointer(a); + int32KEY *ib = (int32KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int4_abbrev_convert(Datum original, SortSupport ssup) +{ + int32KEY *b1 = (int32KEY *) DatumGetPointer(original); + int32 z = b1->lower; + + return (Datum) z; +} + +static int +gbt_int4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int4_cmp_abbrev; + ssup->abbrev_converter = gbt_int4_abbrev_convert; + ssup->abbrev_abort = gbt_int4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int4_cmp; + } + else + { + ssup->comparator = gbt_int4_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c index df2b0d174b..32f8c3b8b4 100644 --- a/contrib/btree_gist/btree_int8.c +++ b/contrib/btree_gist/btree_int8.c @@ -24,7 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int8_consistent); PG_FUNCTION_INFO_V1(gbt_int8_distance); PG_FUNCTION_INFO_V1(gbt_int8_penalty); PG_FUNCTION_INFO_V1(gbt_int8_same); - +PG_FUNCTION_INFO_V1(gbt_int8_sortsupport); static bool gbt_int8gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -215,3 +215,76 @@ gbt_int8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int8_cmp(Datum a, Datum b, SortSupport ssup) +{ + int64KEY *ia = (int64KEY *) DatumGetPointer(a); + int64KEY *ib = (int64KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int8_abbrev_convert(Datum original, SortSupport ssup) +{ + int64KEY *b1 = (int64KEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gbt_int8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int8_cmp_abbrev; + ssup->abbrev_converter = gbt_int8_abbrev_convert; + ssup->abbrev_abort = gbt_int8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int8_cmp; + } + else + { + ssup->comparator = gbt_int8_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_macaddr.c b/contrib/btree_gist/btree_macaddr.c index 7f0e9e9c91..d91369e260 100644 --- a/contrib/btree_gist/btree_macaddr.c +++ b/contrib/btree_gist/btree_macaddr.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad_picksplit); PG_FUNCTION_INFO_V1(gbt_macad_consistent); PG_FUNCTION_INFO_V1(gbt_macad_penalty); PG_FUNCTION_INFO_V1(gbt_macad_same); +PG_FUNCTION_INFO_V1(gbt_macad_sortsupport); static bool @@ -195,3 +196,44 @@ gbt_macad_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad_cmp(Datum a, Datum b, SortSupport ssup) +{ + macKEY *ma = (macKEY *) DatumGetPointer(a); + macKEY *mb = (macKEY *) DatumGetPointer(b); + uint64 ia = mac_2_uint64(&ma->lower); + uint64 ib = mac_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + { + return 0; + } + + return (ia > ib) ? 1 : -1; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + } + else + { + ssup->comparator = gbt_macad_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_macaddr8.c b/contrib/btree_gist/btree_macaddr8.c index ab4bca5d50..604871d789 100644 --- a/contrib/btree_gist/btree_macaddr8.c +++ b/contrib/btree_gist/btree_macaddr8.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad8_picksplit); PG_FUNCTION_INFO_V1(gbt_macad8_consistent); PG_FUNCTION_INFO_V1(gbt_macad8_penalty); PG_FUNCTION_INFO_V1(gbt_macad8_same); +PG_FUNCTION_INFO_V1(gbt_macad8_sortsupport); static bool @@ -195,3 +196,43 @@ gbt_macad8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad8_cmp(Datum a, Datum b, SortSupport ssup) +{ + mac8KEY *ma = (mac8KEY *) DatumGetPointer(a); + mac8KEY *mb = (mac8KEY *) DatumGetPointer(b); + uint64 ia = mac8_2_uint64(&ma->lower); + uint64 ib = mac8_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + { + return 0; + } + + return (ia > ib) ? 1 : -1; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad8_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + } + else + { + ssup->comparator = gbt_macad8_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_numeric.c b/contrib/btree_gist/btree_numeric.c index 35e466cdd9..f1f33793c5 100644 --- a/contrib/btree_gist/btree_numeric.c +++ b/contrib/btree_gist/btree_numeric.c @@ -21,6 +21,7 @@ PG_FUNCTION_INFO_V1(gbt_numeric_picksplit); PG_FUNCTION_INFO_V1(gbt_numeric_consistent); PG_FUNCTION_INFO_V1(gbt_numeric_penalty); PG_FUNCTION_INFO_V1(gbt_numeric_same); +PG_FUNCTION_INFO_V1(gbt_numeric_sortsupport); /* define for comparison */ @@ -227,3 +228,33 @@ gbt_numeric_picksplit(PG_FUNCTION_ARGS) &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(v); } + +static int +gbt_numeric_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(numeric_cmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_numeric_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_numeric_cmp_sort; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + } + else + { + ssup->comparator = gbt_numeric_cmp_sort; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c index 3cc7d4245d..5dcfc2ec48 100644 --- a/contrib/btree_gist/btree_oid.c +++ b/contrib/btree_gist/btree_oid.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_oid_consistent); PG_FUNCTION_INFO_V1(gbt_oid_distance); PG_FUNCTION_INFO_V1(gbt_oid_penalty); PG_FUNCTION_INFO_V1(gbt_oid_same); +PG_FUNCTION_INFO_V1(gbt_oid_sortsupport); static bool @@ -215,3 +216,73 @@ gbt_oid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_oid_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_oid_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + Oid z = b1->lower; + + return (Datum) z; +} + +static int +gbt_oid_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_oid_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_oid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_oid_cmp_abbrev; + ssup->abbrev_converter = gbt_oid_abbrev_convert; + ssup->abbrev_abort = gbt_oid_abbrev_abort; + ssup->abbrev_full_comparator = gbt_oid_cmp; + } + else + { + ssup->comparator = gbt_oid_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 8019d11281..78ecf4e18e 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_text_consistent); PG_FUNCTION_INFO_V1(gbt_bpchar_consistent); PG_FUNCTION_INFO_V1(gbt_text_penalty); PG_FUNCTION_INFO_V1(gbt_text_same); +PG_FUNCTION_INFO_V1(gbt_text_sortsupport); /* define for comparison */ @@ -239,3 +240,35 @@ gbt_text_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_text_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2Coll(bttextcmp, + ssup->ssup_collation, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_text_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_text_cmp_sort; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + } + else + { + ssup->comparator = gbt_text_cmp_sort; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c index fd8774a2f0..6198a6e104 100644 --- a/contrib/btree_gist/btree_time.c +++ b/contrib/btree_gist/btree_time.c @@ -28,6 +28,7 @@ PG_FUNCTION_INFO_V1(gbt_time_distance); PG_FUNCTION_INFO_V1(gbt_timetz_consistent); PG_FUNCTION_INFO_V1(gbt_time_penalty); PG_FUNCTION_INFO_V1(gbt_time_same); +PG_FUNCTION_INFO_V1(gbt_time_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -332,3 +333,36 @@ gbt_time_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_time_cmp_sort(Datum a, Datum b, SortSupport ssup) +{ + timeKEY *ia = (timeKEY *) DatumGetPointer(a); + timeKEY *ib = (timeKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(time_cmp, TimeADTGetDatumFast(ia->lower), TimeADTGetDatumFast(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_time_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_time_cmp_sort; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + } + else + { + ssup->comparator = gbt_time_cmp_sort; + } + PG_RETURN_VOID(); +} + + diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c index 2671ba961c..6e699ed198 100644 --- a/contrib/btree_gist/btree_ts.c +++ b/contrib/btree_gist/btree_ts.c @@ -31,6 +31,7 @@ PG_FUNCTION_INFO_V1(gbt_tstz_consistent); PG_FUNCTION_INFO_V1(gbt_tstz_distance); PG_FUNCTION_INFO_V1(gbt_ts_penalty); PG_FUNCTION_INFO_V1(gbt_ts_same); +PG_FUNCTION_INFO_V1(gbt_ts_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -399,3 +400,35 @@ gbt_ts_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_ts_cmp(Datum a, Datum b, SortSupport ssup) +{ + tsKEY *ia = (tsKEY *) DatumGetPointer(a); + tsKEY *ib = (tsKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(timestamp_cmp, TimestampGetDatumFast(ia->lower), TimestampGetDatumFast(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_ts_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_ts_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + } + else + { + ssup->comparator = gbt_ts_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_uuid.c b/contrib/btree_gist/btree_uuid.c index b81875979a..e99432bceb 100644 --- a/contrib/btree_gist/btree_uuid.c +++ b/contrib/btree_gist/btree_uuid.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_uuid_picksplit); PG_FUNCTION_INFO_V1(gbt_uuid_consistent); PG_FUNCTION_INFO_V1(gbt_uuid_penalty); PG_FUNCTION_INFO_V1(gbt_uuid_same); +PG_FUNCTION_INFO_V1(gbt_uuid_sortsupport); static int @@ -233,3 +234,35 @@ gbt_uuid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_uuid_cmp(Datum a, Datum b, SortSupport ssup) +{ + uuidKEY *ua = (uuidKEY *) DatumGetPointer(a); + uuidKEY *ub = (uuidKEY *) DatumGetPointer(b); + + return uuid_internal_cmp(&ua->lower, &ub->lower); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_uuid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_uuid_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + } + else + { + ssup->comparator = gbt_uuid_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index 8a14620fab..e600015b12 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -338,6 +338,7 @@ gistadjustmembers(Oid opfamilyoid, case GIST_DISTANCE_PROC: case GIST_FETCH_PROC: case GIST_OPTIONS_PROC: + case GIST_SORTSUPPORT_PROC: /* Optional, so force it to be a soft family dependency */ op->ref_is_hard = false; op->ref_is_family = true; -- 2.24.3 (Apple Git-128) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-11-02 14:45  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2020-11-02 14:45 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 30/10/2020 20:20, Andrey Borodin wrote:
>> 27 окт. 2020 г., в 16:43, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
>>> static bool
>>> gbt_inet_abbrev_abort(int memtupcount, SortSupport ssup)
>>> {
>>> #if SIZEOF_DATUM == 8
>>> 	return false;
>>> #else
>>> 	return true;
>>> #endif
>>> }
>>
>> Better to not set the 'abbrev_converter' function in the first place. Or would it be better to cast the float8 to float4 if SIZEOF_DATUM == 4?
> Ok, now for 4 bytes Datum we do return (Datum) Float4GetDatum((float) z);

A few quick comments:

* Currently with float8, you immediately abort abbreviation if 
SIZEOF_DATUM == 4. Like in the 'inet' above, you could convert the 
float8 to float4 instead.

* Some of the ALTER OPERATOR FAMILY commands in btree_gist--1.6--1.7.sql 
are copy-pasted wrong. Here's one example:

ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
	FUNCTION	11	(text, text) gbt_text_sortsupport;

* It's easy to confuse the new comparison functions with the existing 
comparisons used by the picksplit functions. Some comments and/or naming 
changes would be good to clarify how they're different.

* It would be straightforward to have abbreviated functions for macaddr 
and macaddr8 too.

> How do you think, should this patch and patch with pageinspect GiST functions be registered on commitfest?

Yeah, that'd be good.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-11-05 17:11  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2020-11-05 17:11 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 2 нояб. 2020 г., в 19:45, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
>> How do you think, should this patch and patch with pageinspect GiST functions be registered on commitfest?
> 
> Yeah, that'd be good.
I've registered both patches on January CF.
pageinspect patch's code looks goot to me (besides TODO item there), but it lacks docs and tests. I can add some info and calls in future reviews.

> 
> On 30/10/2020 20:20, Andrey Borodin wrote:
> A few quick comments:
> 
> * Currently with float8, you immediately abort abbreviation if SIZEOF_DATUM == 4. Like in the 'inet' above, you could convert the float8 to float4 instead.
> 
> * Some of the ALTER OPERATOR FAMILY commands in btree_gist--1.6--1.7.sql are copy-pasted wrong. Here's one example:
> 
> ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
> 	FUNCTION	11	(text, text) gbt_text_sortsupport;
> 
> * It's easy to confuse the new comparison functions with the existing comparisons used by the picksplit functions. Some comments and/or naming changes would be good to clarify how they're different.
> 
> * It would be straightforward to have abbreviated functions for macaddr and macaddr8 too.

I'll fix these issues soon. But things like this bother me a lot:
> ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
> 	FUNCTION	11	(text, text) gbt_text_sortsupport;

To test that functions are actually called for sorting build we should support directive sorting build like "CREATE INDEX ON A USING GIST(B) WITH(sorting=surely,and fail if not)".
If we have unconditional sorting build and unconditional buffered build we can check opclasses code better.
The problem is current reloption is called "buffering". It would be strange if we gave this enum possible value like "not buffering, but very much like buffering, just another way".
How do you think, is it ok to add reloption "buffering=sorting" to enhance tests?

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-11-05 17:20  Justin Pryzby <pryzby@telsasoft.com>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Justin Pryzby @ 2020-11-05 17:20 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Tom Lane <tgl@sss.pgh.pa.us>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Thu, Nov 05, 2020 at 10:11:52PM +0500, Andrey Borodin wrote:
> To test that functions are actually called for sorting build we should support directive sorting build like "CREATE INDEX ON A USING GIST(B) WITH(sorting=surely,and fail if not)".

Maybe you could add a DEBUG1 message for that, and include that in regression
tests, which would then fail if sorting wasn't used.

Maybe you'd need to make it consistent by setting GUCs like work_mem /
max_parallel_maintenance_workers / ??

Similar to this

postgres=# SET client_min_messages =debug;
postgres=# CREATE INDEX ON A USING GIST(i) WITH(buffering=off);
DEBUG:  building index "a_i_idx2" on table "a" serially
CREATE INDEX

> If we have unconditional sorting build and unconditional buffered build we can check opclasses code better.
> The problem is current reloption is called "buffering". It would be strange if we gave this enum possible value like "not buffering, but very much like buffering, just another way".
> How do you think, is it ok to add reloption "buffering=sorting" to enhance tests?
> 
> Best regards, Andrey Borodin.





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-11-07 15:42  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Justin Pryzby <pryzby@telsasoft.com>
  0 siblings, 0 replies; 111+ messages in thread

From: Andrey Borodin @ 2020-11-07 15:42 UTC (permalink / raw)
  To: Justin Pryzby <pryzby@telsasoft.com>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Tom Lane <tgl@sss.pgh.pa.us>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 5 нояб. 2020 г., в 22:20, Justin Pryzby <pryzby@telsasoft.com> написал(а):
> 
> On Thu, Nov 05, 2020 at 10:11:52PM +0500, Andrey Borodin wrote:
>> To test that functions are actually called for sorting build we should support directive sorting build like "CREATE INDEX ON A USING GIST(B) WITH(sorting=surely,and fail if not)".
> 
> Maybe you could add a DEBUG1 message for that, and include that in regression
> tests, which would then fail if sorting wasn't used.

That's a good idea. Thanks!
> 
> Maybe you'd need to make it consistent by setting GUCs like work_mem /
> max_parallel_maintenance_workers / ??
> 
> Similar to this
> 
> postgres=# SET client_min_messages =debug;
> postgres=# CREATE INDEX ON A USING GIST(i) WITH(buffering=off);
> DEBUG:  building index "a_i_idx2" on table "a" serially
> CREATE INDEX

Currently, only B-tree uses parallel build, so no need to tweak GUCs except client_min_messages.
Before these tests, actually, ~20% of opclasses were not working as expected. Despite I've checked each one by hand. I have 

PFA patch with fixed comments from Heikki.

Thanks!

Best regards, Andrey Borodin.

Attachments:

v4-0001-Sortsupport-for-sorting-GiST-build-for-gist_btree.patch (application/octet-stream, 81.6K) download From 5360dac5b4c15a01b55a93dab6048b71df1b5dbb Mon Sep 17 00:00:00 2001 From: Andrey Borodin <amborodin@acm.org> Date: Sat, 3 Oct 2020 21:08:54 +0500 Subject: [PATCH v4] Sortsupport for sorting GiST build for gist_btree types --- contrib/btree_gist/Makefile | 2 +- contrib/btree_gist/btree_bit.c | 24 +++ contrib/btree_gist/btree_bytea.c | 26 ++- contrib/btree_gist/btree_cash.c | 75 ++++++++ contrib/btree_gist/btree_date.c | 28 +++ contrib/btree_gist/btree_enum.c | 70 ++++++++ contrib/btree_gist/btree_float4.c | 71 ++++++++ contrib/btree_gist/btree_float8.c | 80 +++++++++ contrib/btree_gist/btree_gist--1.6--1.7.sql | 182 ++++++++++++++++++++ contrib/btree_gist/btree_gist.control | 2 +- contrib/btree_gist/btree_gist.h | 1 + contrib/btree_gist/btree_inet.c | 81 +++++++++ contrib/btree_gist/btree_int2.c | 70 ++++++++ contrib/btree_gist/btree_int4.c | 70 ++++++++ contrib/btree_gist/btree_int8.c | 75 +++++++- contrib/btree_gist/btree_interval.c | 25 +++ contrib/btree_gist/btree_macaddr.c | 71 ++++++++ contrib/btree_gist/btree_macaddr8.c | 71 ++++++++ contrib/btree_gist/btree_numeric.c | 24 +++ contrib/btree_gist/btree_oid.c | 71 ++++++++ contrib/btree_gist/btree_text.c | 26 +++ contrib/btree_gist/btree_time.c | 27 +++ contrib/btree_gist/btree_ts.c | 26 +++ contrib/btree_gist/btree_uuid.c | 26 +++ contrib/btree_gist/expected/bit.out | 7 + contrib/btree_gist/expected/bytea.out | 7 + contrib/btree_gist/expected/cash.out | 7 + contrib/btree_gist/expected/char.out | 7 + contrib/btree_gist/expected/cidr.out | 7 + contrib/btree_gist/expected/date.out | 7 + contrib/btree_gist/expected/enum.out | 7 + contrib/btree_gist/expected/float4.out | 7 + contrib/btree_gist/expected/float8.out | 7 + contrib/btree_gist/expected/inet.out | 7 + contrib/btree_gist/expected/int2.out | 7 + contrib/btree_gist/expected/int4.out | 7 + contrib/btree_gist/expected/int8.out | 7 + contrib/btree_gist/expected/interval.out | 7 + contrib/btree_gist/expected/macaddr.out | 7 + contrib/btree_gist/expected/macaddr8.out | 7 + contrib/btree_gist/expected/numeric.out | 7 + contrib/btree_gist/expected/oid.out | 7 + contrib/btree_gist/expected/text.out | 7 + contrib/btree_gist/expected/time.out | 7 + contrib/btree_gist/expected/timestamp.out | 7 + contrib/btree_gist/expected/timestamptz.out | 7 + contrib/btree_gist/expected/timetz.out | 7 + contrib/btree_gist/expected/uuid.out | 7 + contrib/btree_gist/expected/varbit.out | 7 + contrib/btree_gist/expected/varchar.out | 7 + contrib/btree_gist/sql/bit.sql | 4 + contrib/btree_gist/sql/bytea.sql | 4 + contrib/btree_gist/sql/cash.sql | 4 + contrib/btree_gist/sql/char.sql | 4 + contrib/btree_gist/sql/cidr.sql | 4 + contrib/btree_gist/sql/date.sql | 4 + contrib/btree_gist/sql/enum.sql | 4 + contrib/btree_gist/sql/float4.sql | 4 + contrib/btree_gist/sql/float8.sql | 4 + contrib/btree_gist/sql/inet.sql | 4 + contrib/btree_gist/sql/int2.sql | 4 + contrib/btree_gist/sql/int4.sql | 4 + contrib/btree_gist/sql/int8.sql | 4 + contrib/btree_gist/sql/interval.sql | 4 + contrib/btree_gist/sql/macaddr.sql | 4 + contrib/btree_gist/sql/macaddr8.sql | 4 + contrib/btree_gist/sql/numeric.sql | 4 + contrib/btree_gist/sql/oid.sql | 4 + contrib/btree_gist/sql/text.sql | 4 + contrib/btree_gist/sql/time.sql | 4 + contrib/btree_gist/sql/timestamp.sql | 4 + contrib/btree_gist/sql/timestamptz.sql | 4 + contrib/btree_gist/sql/timetz.sql | 4 + contrib/btree_gist/sql/uuid.sql | 4 + contrib/btree_gist/sql/varbit.sql | 4 + contrib/btree_gist/sql/varchar.sql | 4 + src/backend/access/gist/gistbuild.c | 1 + 77 files changed, 1507 insertions(+), 4 deletions(-) create mode 100644 contrib/btree_gist/btree_gist--1.6--1.7.sql diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile index e92d974a1a..a1f818f71e 100644 --- a/contrib/btree_gist/Makefile +++ b/contrib/btree_gist/Makefile @@ -32,7 +32,7 @@ EXTENSION = btree_gist DATA = btree_gist--1.0--1.1.sql \ btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \ - btree_gist--1.5--1.6.sql + btree_gist--1.5--1.6.sql btree_gist--1.6--1.7.sql PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes" REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \ diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c index 2225244ded..e5d3ac6a24 100644 --- a/contrib/btree_gist/btree_bit.c +++ b/contrib/btree_gist/btree_bit.c @@ -19,6 +19,7 @@ PG_FUNCTION_INFO_V1(gbt_bit_picksplit); PG_FUNCTION_INFO_V1(gbt_bit_consistent); PG_FUNCTION_INFO_V1(gbt_bit_penalty); PG_FUNCTION_INFO_V1(gbt_bit_same); +PG_FUNCTION_INFO_V1(gbt_bit_sortsupport); /* define for comparison */ @@ -209,3 +210,26 @@ gbt_bit_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bit_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bit_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_bit_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_bytea.c b/contrib/btree_gist/btree_bytea.c index 6b005f0157..a2abfb7d7c 100644 --- a/contrib/btree_gist/btree_bytea.c +++ b/contrib/btree_gist/btree_bytea.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_bytea_picksplit); PG_FUNCTION_INFO_V1(gbt_bytea_consistent); PG_FUNCTION_INFO_V1(gbt_bytea_penalty); PG_FUNCTION_INFO_V1(gbt_bytea_same); +PG_FUNCTION_INFO_V1(gbt_bytea_sortsupport); /* define for comparison */ @@ -87,7 +88,7 @@ static const gbtree_vinfo tinfo = /************************************************** - * Text ops + * Bytea ops **************************************************/ @@ -168,3 +169,26 @@ gbt_bytea_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bytea_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bytea_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_bytea_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c index dfa23224b6..bba5c51a8e 100644 --- a/contrib/btree_gist/btree_cash.c +++ b/contrib/btree_gist/btree_cash.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_cash_consistent); PG_FUNCTION_INFO_V1(gbt_cash_distance); PG_FUNCTION_INFO_V1(gbt_cash_penalty); PG_FUNCTION_INFO_V1(gbt_cash_same); +PG_FUNCTION_INFO_V1(gbt_cash_sortsupport); static bool gbt_cashgt(const void *a, const void *b, FmgrInfo *flinfo) @@ -216,3 +217,77 @@ gbt_cash_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_cash_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + cashKEY *ia = (cashKEY *) DatumGetPointer(a); + cashKEY *ib = (cashKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_cash_abbrev_convert(Datum original, SortSupport ssup) +{ + cashKEY *b1 = (cashKEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gbt_cash_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_cash_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_cash_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_cash_cmp_abbrev; + ssup->abbrev_converter = gbt_cash_abbrev_convert; + ssup->abbrev_abort = gbt_cash_abbrev_abort; + ssup->abbrev_full_comparator = gbt_cash_sort_build_cmp; + } + else + { + ssup->comparator = gbt_cash_sort_build_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c index 455a265a49..7d6759b101 100644 --- a/contrib/btree_gist/btree_date.c +++ b/contrib/btree_gist/btree_date.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_date_consistent); PG_FUNCTION_INFO_V1(gbt_date_distance); PG_FUNCTION_INFO_V1(gbt_date_penalty); PG_FUNCTION_INFO_V1(gbt_date_same); +PG_FUNCTION_INFO_V1(gbt_date_sortsupport); static bool gbt_dategt(const void *a, const void *b, FmgrInfo *flinfo) @@ -257,3 +258,30 @@ gbt_date_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_date_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + dateKEY *ia = (dateKEY *) PointerGetDatum(a); + dateKEY *ib = (dateKEY *) PointerGetDatum(b); + + return DatumGetInt32(DirectFunctionCall2(date_cmp, + DateADTGetDatum(ia->lower), + DateADTGetDatum(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_date_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_date_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_enum.c b/contrib/btree_gist/btree_enum.c index d4dc38a38e..41b487747e 100644 --- a/contrib/btree_gist/btree_enum.c +++ b/contrib/btree_gist/btree_enum.c @@ -26,6 +26,7 @@ PG_FUNCTION_INFO_V1(gbt_enum_picksplit); PG_FUNCTION_INFO_V1(gbt_enum_consistent); PG_FUNCTION_INFO_V1(gbt_enum_penalty); PG_FUNCTION_INFO_V1(gbt_enum_same); +PG_FUNCTION_INFO_V1(gbt_enum_sortsupport); static bool @@ -183,3 +184,72 @@ gbt_enum_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_enum_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_enum_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + Oid z = b1->lower; + + return (Datum) z; +} + +static int +gbt_enum_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_enum_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_enum_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_enum_cmp_abbrev; + ssup->abbrev_converter = gbt_enum_abbrev_convert; + ssup->abbrev_abort = gbt_enum_abbrev_abort; + ssup->abbrev_full_comparator = gbt_enum_sort_build_cmp; + } + else + { + ssup->comparator = gbt_enum_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c index 3604c73313..751e526b0a 100644 --- a/contrib/btree_gist/btree_float4.c +++ b/contrib/btree_gist/btree_float4.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float4_consistent); PG_FUNCTION_INFO_V1(gbt_float4_distance); PG_FUNCTION_INFO_V1(gbt_float4_penalty); PG_FUNCTION_INFO_V1(gbt_float4_same); +PG_FUNCTION_INFO_V1(gbt_float4_sortsupport); static bool gbt_float4gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -209,3 +210,73 @@ gbt_float4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + + +static int +gbt_float4_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + float4KEY *ia = (float4KEY *) DatumGetPointer(a); + float4KEY *ib = (float4KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float4_abbrev_convert(Datum original, SortSupport ssup) +{ + float4KEY *b1 = (float4KEY *) DatumGetPointer(original); + float4 z = b1->lower; + + return (Datum) Float4GetDatum(z); +} + +static int +gbt_float4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (DatumGetFloat4(z1) > DatumGetFloat4(z2)) + return 1; + else if (DatumGetFloat4(z1) < DatumGetFloat4(z2)) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_float4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float4_cmp_abbrev; + ssup->abbrev_converter = gbt_float4_abbrev_convert; + ssup->abbrev_abort = gbt_float4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float4_sort_build_cmp; + } + else + { + ssup->comparator = gbt_float4_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c index 10a5262aaa..b4e6a75361 100644 --- a/contrib/btree_gist/btree_float8.c +++ b/contrib/btree_gist/btree_float8.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float8_consistent); PG_FUNCTION_INFO_V1(gbt_float8_distance); PG_FUNCTION_INFO_V1(gbt_float8_penalty); PG_FUNCTION_INFO_V1(gbt_float8_same); +PG_FUNCTION_INFO_V1(gbt_float8_sortsupport); static bool @@ -216,3 +217,82 @@ gbt_float8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_float8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + float8KEY *ia = (float8KEY *) DatumGetPointer(a); + float8KEY *ib = (float8KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float8_abbrev_convert(Datum original, SortSupport ssup) +{ + float8KEY *b1 = (float8KEY *) DatumGetPointer(original); + float8 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) Float8GetDatum(z); +#else + return (Datum) Float4GetDatum((float)z); +#endif +} + +static int +gbt_float8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + if (DatumGetFloat8(z1) > DatumGetFloat8(z2)) + return 1; + else if (DatumGetFloat8(z1) < DatumGetFloat8(z2)) + return -1; + else + return 0; +#else + if (DatumGetFloat4(z1) > DatumGetFloat4(z2)) + return 1; + else if (DatumGetFloat4(z1) < DatumGetFloat4(z2)) + return -1; + else + return 0; +#endif +} + +static bool +gbt_float8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float8_cmp_abbrev; + ssup->abbrev_converter = gbt_float8_abbrev_convert; + ssup->abbrev_abort = gbt_float8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_float8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_gist--1.6--1.7.sql b/contrib/btree_gist/btree_gist--1.6--1.7.sql new file mode 100644 index 0000000000..abb5b8b0f4 --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.6--1.7.sql @@ -0,0 +1,182 @@ +/* contrib/btree_gist/btree_gist--1.6--1.7.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.7'" to load this file. \quit + + +CREATE FUNCTION gbt_int8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD + FUNCTION 11 (int8, int8) gbt_int8_sortsupport (internal) ; + +CREATE FUNCTION gbt_int4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD + FUNCTION 11 (int4, int4) gbt_int4_sortsupport (internal) ; + +CREATE FUNCTION gbt_int2_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD + FUNCTION 11 (int2, int2) gbt_int2_sortsupport (internal) ; + +CREATE FUNCTION gbt_float8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD + FUNCTION 11 (float8, float8) gbt_float8_sortsupport (internal) ; + +CREATE FUNCTION gbt_float4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD + FUNCTION 11 (float4, float4) gbt_float4_sortsupport (internal) ; + +CREATE FUNCTION gbt_enum_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD + FUNCTION 11 (anyenum, anyenum) gbt_enum_sortsupport (internal) ; + +CREATE FUNCTION gbt_oid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD + FUNCTION 11 (oid, oid) gbt_oid_sortsupport (internal) ; + +CREATE FUNCTION gbt_cash_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD + FUNCTION 11 (money, money) gbt_cash_sortsupport (internal) ; + +CREATE FUNCTION gbt_inet_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD + FUNCTION 11 (inet, inet) gbt_inet_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD + FUNCTION 11 (cidr, cidr) gbt_inet_sortsupport (internal) ; + + +CREATE FUNCTION gbt_macad_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD + FUNCTION 11 (macaddr, macaddr) gbt_macad_sortsupport (internal) ; + +CREATE FUNCTION gbt_macad8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD + FUNCTION 11 (macaddr8, macaddr8) gbt_macad8_sortsupport (internal) ; + +CREATE FUNCTION gbt_numeric_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD + FUNCTION 11 (numeric, numeric) gbt_numeric_sortsupport (internal) ; + +CREATE FUNCTION gbt_uuid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD + FUNCTION 11 (uuid, uuid) gbt_uuid_sortsupport (internal) ; + +CREATE FUNCTION gbt_ts_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD + FUNCTION 11 (timestamp, timestamp) gbt_ts_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (timestamptz, timestamptz) gbt_ts_sortsupport (internal) ; + +CREATE FUNCTION gbt_text_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_text_ops USING gist ADD + FUNCTION 11 (text, text) gbt_text_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD + FUNCTION 11 (bpchar, bpchar) gbt_text_sortsupport (internal) ; + +CREATE FUNCTION gbt_time_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_time_ops USING gist ADD + FUNCTION 11 (time, time) gbt_time_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD + FUNCTION 11 (timetz, timetz) gbt_time_sortsupport (internal) ; + +CREATE FUNCTION gbt_bytea_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD + FUNCTION 11 (bytea, bytea) gbt_bytea_sortsupport (internal) ; + +CREATE FUNCTION gbt_date_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_date_ops USING gist ADD + FUNCTION 11 (date, date) gbt_date_sortsupport (internal) ; + +CREATE FUNCTION gbt_bit_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD + FUNCTION 11 (bit, bit) gbt_bit_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD + FUNCTION 11 (varbit, varbit) gbt_bit_sortsupport (internal) ; + +CREATE FUNCTION gbt_intv_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD + FUNCTION 11 (interval, interval) gbt_intv_sortsupport (internal) ; + diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control index e5c41fe8f3..fa9171a80a 100644 --- a/contrib/btree_gist/btree_gist.control +++ b/contrib/btree_gist/btree_gist.control @@ -1,6 +1,6 @@ # btree_gist extension comment = 'support for indexing common datatypes in GiST' -default_version = '1.6' +default_version = '1.7' module_pathname = '$libdir/btree_gist' relocatable = true trusted = true diff --git a/contrib/btree_gist/btree_gist.h b/contrib/btree_gist/btree_gist.h index 14c7c8ee19..35ad287ed3 100644 --- a/contrib/btree_gist/btree_gist.h +++ b/contrib/btree_gist/btree_gist.h @@ -6,6 +6,7 @@ #include "access/nbtree.h" #include "fmgr.h" +#include "utils/sortsupport.h" #define BtreeGistNotEqualStrategyNumber 6 diff --git a/contrib/btree_gist/btree_inet.c b/contrib/btree_gist/btree_inet.c index e4b3a946b2..d3188f65c3 100644 --- a/contrib/btree_gist/btree_inet.c +++ b/contrib/btree_gist/btree_inet.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_inet_picksplit); PG_FUNCTION_INFO_V1(gbt_inet_consistent); PG_FUNCTION_INFO_V1(gbt_inet_penalty); PG_FUNCTION_INFO_V1(gbt_inet_same); +PG_FUNCTION_INFO_V1(gbt_inet_sortsupport); static bool @@ -186,3 +187,83 @@ gbt_inet_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_inet_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + inetKEY *ia = (inetKEY *) DatumGetPointer(a); + inetKEY *ib = (inetKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_inet_abbrev_convert(Datum original, SortSupport ssup) +{ + inetKEY *b1 = (inetKEY *) DatumGetPointer(original); + double z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) Float8GetDatum(z); +#else + return (Datum) Float4GetDatum((float) z); +#endif +} + +static int +gbt_inet_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + if (DatumGetFloat8(z1) > DatumGetFloat8(z2)) + return 1; + else if (DatumGetFloat8(z1) < DatumGetFloat8(z2)) + return -1; + else + return 0; +#else + if (DatumGetFloat4(z1) > DatumGetFloat4(z2)) + return 1; + else if (DatumGetFloat4(z1) < DatumGetFloat4(z2)) + return -1; + else + return 0; +#endif +} + +static bool +gbt_inet_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_inet_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_inet_cmp_abbrev; + ssup->abbrev_converter = gbt_inet_abbrev_convert; + ssup->abbrev_abort = gbt_inet_abbrev_abort; + ssup->abbrev_full_comparator = gbt_inet_sort_build_cmp; + } + else + { + ssup->comparator = gbt_inet_sort_build_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c index a91b95ff39..968c6950ca 100644 --- a/contrib/btree_gist/btree_int2.c +++ b/contrib/btree_gist/btree_int2.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int2_consistent); PG_FUNCTION_INFO_V1(gbt_int2_distance); PG_FUNCTION_INFO_V1(gbt_int2_penalty); PG_FUNCTION_INFO_V1(gbt_int2_same); +PG_FUNCTION_INFO_V1(gbt_int2_sortsupport); static bool gbt_int2gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -214,3 +215,72 @@ gbt_int2_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int2_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int16KEY *ia = (int16KEY *) DatumGetPointer(a); + int16KEY *ib = (int16KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int2_abbrev_convert(Datum original, SortSupport ssup) +{ + int16KEY *b1 = (int16KEY *) DatumGetPointer(original); + int16 z = b1->lower; + + return (Datum) z; +} + +static int +gbt_int2_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int2_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int2_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int2_cmp_abbrev; + ssup->abbrev_converter = gbt_int2_abbrev_convert; + ssup->abbrev_abort = gbt_int2_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int2_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int2_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c index 7ea98c478c..9f6f77ce43 100644 --- a/contrib/btree_gist/btree_int4.c +++ b/contrib/btree_gist/btree_int4.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int4_consistent); PG_FUNCTION_INFO_V1(gbt_int4_distance); PG_FUNCTION_INFO_V1(gbt_int4_penalty); PG_FUNCTION_INFO_V1(gbt_int4_same); +PG_FUNCTION_INFO_V1(gbt_int4_sortsupport); static bool @@ -215,3 +216,72 @@ gbt_int4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int4_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int32KEY *ia = (int32KEY *) DatumGetPointer(a); + int32KEY *ib = (int32KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int4_abbrev_convert(Datum original, SortSupport ssup) +{ + int32KEY *b1 = (int32KEY *) DatumGetPointer(original); + int32 z = b1->lower; + + return (Datum) z; +} + +static int +gbt_int4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int4_cmp_abbrev; + ssup->abbrev_converter = gbt_int4_abbrev_convert; + ssup->abbrev_abort = gbt_int4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int4_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int4_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c index df2b0d174b..18be500ab1 100644 --- a/contrib/btree_gist/btree_int8.c +++ b/contrib/btree_gist/btree_int8.c @@ -24,7 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int8_consistent); PG_FUNCTION_INFO_V1(gbt_int8_distance); PG_FUNCTION_INFO_V1(gbt_int8_penalty); PG_FUNCTION_INFO_V1(gbt_int8_same); - +PG_FUNCTION_INFO_V1(gbt_int8_sortsupport); static bool gbt_int8gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -215,3 +215,76 @@ gbt_int8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int64KEY *ia = (int64KEY *) DatumGetPointer(a); + int64KEY *ib = (int64KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int8_abbrev_convert(Datum original, SortSupport ssup) +{ + int64KEY *b1 = (int64KEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gbt_int8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int8_cmp_abbrev; + ssup->abbrev_converter = gbt_int8_abbrev_convert; + ssup->abbrev_abort = gbt_int8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c index a4b3b2b1e6..2657213ec7 100644 --- a/contrib/btree_gist/btree_interval.c +++ b/contrib/btree_gist/btree_interval.c @@ -27,6 +27,7 @@ PG_FUNCTION_INFO_V1(gbt_intv_consistent); PG_FUNCTION_INFO_V1(gbt_intv_distance); PG_FUNCTION_INFO_V1(gbt_intv_penalty); PG_FUNCTION_INFO_V1(gbt_intv_same); +PG_FUNCTION_INFO_V1(gbt_intv_sortsupport); static bool @@ -297,3 +298,27 @@ gbt_intv_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_intv_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + intvKEY *ia = (intvKEY *) DatumGetPointer(a); + intvKEY *ib = (intvKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(interval_cmp, IntervalPGetDatum(&ia->lower), IntervalPGetDatum(&ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_intv_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_intv_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_macaddr.c b/contrib/btree_gist/btree_macaddr.c index 7f0e9e9c91..f43edad74e 100644 --- a/contrib/btree_gist/btree_macaddr.c +++ b/contrib/btree_gist/btree_macaddr.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad_picksplit); PG_FUNCTION_INFO_V1(gbt_macad_consistent); PG_FUNCTION_INFO_V1(gbt_macad_penalty); PG_FUNCTION_INFO_V1(gbt_macad_same); +PG_FUNCTION_INFO_V1(gbt_macad_sortsupport); static bool @@ -195,3 +196,73 @@ gbt_macad_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + macKEY *ma = (macKEY *) DatumGetPointer(a); + macKEY *mb = (macKEY *) DatumGetPointer(b); + uint64 ia = mac_2_uint64(&ma->lower); + uint64 ib = mac_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + { + return 0; + } + + return (ia > ib) ? 1 : -1; +} + +static Datum +gbt_macad_abbrev_convert(Datum original, SortSupport ssup) +{ + macKEY *b1 = (macKEY *) DatumGetPointer(original); + uint64 z = mac_2_uint64(&b1->lower); + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) z>>32; +#endif +} + +static int +gbt_macad_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +static bool +gbt_macad_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad_cmp_abbrev; + ssup->abbrev_converter = gbt_macad_abbrev_convert; + ssup->abbrev_abort = gbt_macad_abbrev_abort; + ssup->abbrev_full_comparator = gbt_macad_sort_build_cmp; + } + else + { + ssup->comparator = gbt_macad_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_macaddr8.c b/contrib/btree_gist/btree_macaddr8.c index ab4bca5d50..458c0bf6f7 100644 --- a/contrib/btree_gist/btree_macaddr8.c +++ b/contrib/btree_gist/btree_macaddr8.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad8_picksplit); PG_FUNCTION_INFO_V1(gbt_macad8_consistent); PG_FUNCTION_INFO_V1(gbt_macad8_penalty); PG_FUNCTION_INFO_V1(gbt_macad8_same); +PG_FUNCTION_INFO_V1(gbt_macad8_sortsupport); static bool @@ -195,3 +196,73 @@ gbt_macad8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + mac8KEY *ma = (mac8KEY *) DatumGetPointer(a); + mac8KEY *mb = (mac8KEY *) DatumGetPointer(b); + uint64 ia = mac8_2_uint64(&ma->lower); + uint64 ib = mac8_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + { + return 0; + } + + return (ia > ib) ? 1 : -1; +} + +static Datum +gbt_macad8_abbrev_convert(Datum original, SortSupport ssup) +{ + mac8KEY *b1 = (mac8KEY *) DatumGetPointer(original); + uint64 z = mac8_2_uint64(&b1->lower); + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) z>>32; +#endif +} + +static int +gbt_macad8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +static bool +gbt_macad8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad8_cmp_abbrev; + ssup->abbrev_converter = gbt_macad8_abbrev_convert; + ssup->abbrev_abort = gbt_macad8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_macad8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_macad8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_numeric.c b/contrib/btree_gist/btree_numeric.c index 35e466cdd9..ed5305397a 100644 --- a/contrib/btree_gist/btree_numeric.c +++ b/contrib/btree_gist/btree_numeric.c @@ -21,6 +21,7 @@ PG_FUNCTION_INFO_V1(gbt_numeric_picksplit); PG_FUNCTION_INFO_V1(gbt_numeric_consistent); PG_FUNCTION_INFO_V1(gbt_numeric_penalty); PG_FUNCTION_INFO_V1(gbt_numeric_same); +PG_FUNCTION_INFO_V1(gbt_numeric_sortsupport); /* define for comparison */ @@ -227,3 +228,26 @@ gbt_numeric_picksplit(PG_FUNCTION_ARGS) &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(v); } + +static int +gbt_numeric_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(numeric_cmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_numeric_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_numeric_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c index 3cc7d4245d..7cad25ba38 100644 --- a/contrib/btree_gist/btree_oid.c +++ b/contrib/btree_gist/btree_oid.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_oid_consistent); PG_FUNCTION_INFO_V1(gbt_oid_distance); PG_FUNCTION_INFO_V1(gbt_oid_penalty); PG_FUNCTION_INFO_V1(gbt_oid_same); +PG_FUNCTION_INFO_V1(gbt_oid_sortsupport); static bool @@ -215,3 +216,73 @@ gbt_oid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_oid_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_oid_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + Oid z = b1->lower; + + return (Datum) z; +} + +static int +gbt_oid_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_oid_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_oid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_oid_cmp_abbrev; + ssup->abbrev_converter = gbt_oid_abbrev_convert; + ssup->abbrev_abort = gbt_oid_abbrev_abort; + ssup->abbrev_full_comparator = gbt_oid_sort_build_cmp; + } + else + { + ssup->comparator = gbt_oid_sort_build_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 8019d11281..1a3fa2a39a 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_text_consistent); PG_FUNCTION_INFO_V1(gbt_bpchar_consistent); PG_FUNCTION_INFO_V1(gbt_text_penalty); PG_FUNCTION_INFO_V1(gbt_text_same); +PG_FUNCTION_INFO_V1(gbt_text_sortsupport); /* define for comparison */ @@ -239,3 +240,28 @@ gbt_text_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_text_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2Coll(bttextcmp, + ssup->ssup_collation, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_text_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_text_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c index fd8774a2f0..7d6009150b 100644 --- a/contrib/btree_gist/btree_time.c +++ b/contrib/btree_gist/btree_time.c @@ -28,6 +28,7 @@ PG_FUNCTION_INFO_V1(gbt_time_distance); PG_FUNCTION_INFO_V1(gbt_timetz_consistent); PG_FUNCTION_INFO_V1(gbt_time_penalty); PG_FUNCTION_INFO_V1(gbt_time_same); +PG_FUNCTION_INFO_V1(gbt_time_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -332,3 +333,29 @@ gbt_time_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_time_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + timeKEY *ia = (timeKEY *) DatumGetPointer(a); + timeKEY *ib = (timeKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(time_cmp, TimeADTGetDatumFast(ia->lower), TimeADTGetDatumFast(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_time_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_time_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} + + diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c index 2671ba961c..31b2024c32 100644 --- a/contrib/btree_gist/btree_ts.c +++ b/contrib/btree_gist/btree_ts.c @@ -31,6 +31,7 @@ PG_FUNCTION_INFO_V1(gbt_tstz_consistent); PG_FUNCTION_INFO_V1(gbt_tstz_distance); PG_FUNCTION_INFO_V1(gbt_ts_penalty); PG_FUNCTION_INFO_V1(gbt_ts_same); +PG_FUNCTION_INFO_V1(gbt_ts_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -399,3 +400,28 @@ gbt_ts_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_ts_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + tsKEY *ia = (tsKEY *) DatumGetPointer(a); + tsKEY *ib = (tsKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(timestamp_cmp, TimestampGetDatumFast(ia->lower), TimestampGetDatumFast(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_ts_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_ts_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_uuid.c b/contrib/btree_gist/btree_uuid.c index b81875979a..43a8cd896a 100644 --- a/contrib/btree_gist/btree_uuid.c +++ b/contrib/btree_gist/btree_uuid.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_uuid_picksplit); PG_FUNCTION_INFO_V1(gbt_uuid_consistent); PG_FUNCTION_INFO_V1(gbt_uuid_penalty); PG_FUNCTION_INFO_V1(gbt_uuid_same); +PG_FUNCTION_INFO_V1(gbt_uuid_sortsupport); static int @@ -233,3 +234,28 @@ gbt_uuid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_uuid_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + uuidKEY *ua = (uuidKEY *) DatumGetPointer(a); + uuidKEY *ub = (uuidKEY *) DatumGetPointer(b); + + return uuid_internal_cmp(&ua->lower, &ub->lower); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_uuid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_uuid_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/expected/bit.out b/contrib/btree_gist/expected/bit.out index e57871f310..fca3a20eec 100644 --- a/contrib/btree_gist/expected/bit.out +++ b/contrib/btree_gist/expected/bit.out @@ -32,7 +32,14 @@ SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; 350 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); +DEBUG: building index "bitidx" on table "bittmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "bitidx_b" on table "bittmp" serially +DROP INDEX bitidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM bittmp WHERE a < '011011000100010111011000110000100'; count diff --git a/contrib/btree_gist/expected/bytea.out b/contrib/btree_gist/expected/bytea.out index b9efa73c08..d40974025e 100644 --- a/contrib/btree_gist/expected/bytea.out +++ b/contrib/btree_gist/expected/bytea.out @@ -33,7 +33,14 @@ SELECT count(*) FROM byteatmp WHERE a > '31b0'; 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); +DEBUG: building index "byteaidx" on table "byteatmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "byteaidx_b" on table "byteatmp" serially +DROP INDEX byteaidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM byteatmp WHERE a < '31b0'::bytea; count diff --git a/contrib/btree_gist/expected/cash.out b/contrib/btree_gist/expected/cash.out index 7fbc735592..650a5b8635 100644 --- a/contrib/btree_gist/expected/cash.out +++ b/contrib/btree_gist/expected/cash.out @@ -40,7 +40,14 @@ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; $21,915.01 | $442.22 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); +DEBUG: building index "moneyidx" on table "moneytmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "moneyidx_b" on table "moneytmp" serially +DROP INDEX moneyidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM moneytmp WHERE a < '22649.64'::money; count diff --git a/contrib/btree_gist/expected/char.out b/contrib/btree_gist/expected/char.out index d715c045cc..9ead7907cf 100644 --- a/contrib/btree_gist/expected/char.out +++ b/contrib/btree_gist/expected/char.out @@ -32,7 +32,14 @@ SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); +DEBUG: building index "charidx" on table "chartmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "charidx_b" on table "chartmp" serially +DROP INDEX charidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM chartmp WHERE a < '31b0'::char(32); count diff --git a/contrib/btree_gist/expected/cidr.out b/contrib/btree_gist/expected/cidr.out index 6d0995add6..fd6b3d9657 100644 --- a/contrib/btree_gist/expected/cidr.out +++ b/contrib/btree_gist/expected/cidr.out @@ -32,7 +32,14 @@ SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; 309 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); +DEBUG: building index "cidridx" on table "cidrtmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "cidridx_b" on table "cidrtmp" serially +DROP INDEX cidridx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM cidrtmp WHERE a < '121.111.63.82'::cidr; count diff --git a/contrib/btree_gist/expected/date.out b/contrib/btree_gist/expected/date.out index 5db864bb82..df7e1b3d3e 100644 --- a/contrib/btree_gist/expected/date.out +++ b/contrib/btree_gist/expected/date.out @@ -40,7 +40,14 @@ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; 03-24-2001 | 39 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); +DEBUG: building index "dateidx" on table "datetmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "dateidx_b" on table "datetmp" serially +DROP INDEX dateidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM datetmp WHERE a < '2001-02-13'::date; count diff --git a/contrib/btree_gist/expected/enum.out b/contrib/btree_gist/expected/enum.out index c4b769dd4b..15cd580c8e 100644 --- a/contrib/btree_gist/expected/enum.out +++ b/contrib/btree_gist/expected/enum.out @@ -46,7 +46,14 @@ SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; 230 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); +DEBUG: building index "enumidx" on table "enumtmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "enumidx_b" on table "enumtmp" serially +DROP INDEX enumidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow; count diff --git a/contrib/btree_gist/expected/float4.out b/contrib/btree_gist/expected/float4.out index dfe732049e..b3cfa72dba 100644 --- a/contrib/btree_gist/expected/float4.out +++ b/contrib/btree_gist/expected/float4.out @@ -40,7 +40,14 @@ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; -158.17741 | 20.822586 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); +DEBUG: building index "float4idx" on table "float4tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "float4idx_b" on table "float4tmp" serially +DROP INDEX float4idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM float4tmp WHERE a < -179.0::float4; count diff --git a/contrib/btree_gist/expected/float8.out b/contrib/btree_gist/expected/float8.out index ebd0ef3d68..e90a061f68 100644 --- a/contrib/btree_gist/expected/float8.out +++ b/contrib/btree_gist/expected/float8.out @@ -40,7 +40,14 @@ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; -1769.73634 | 120.26366000000007 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); +DEBUG: building index "float8idx" on table "float8tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "float8idx_b" on table "float8tmp" serially +DROP INDEX float8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM float8tmp WHERE a < -1890.0::float8; count diff --git a/contrib/btree_gist/expected/inet.out b/contrib/btree_gist/expected/inet.out index c323d903da..e11d83a955 100644 --- a/contrib/btree_gist/expected/inet.out +++ b/contrib/btree_gist/expected/inet.out @@ -32,7 +32,14 @@ SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; 386 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); +DEBUG: building index "inetidx" on table "inettmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "inetidx_b" on table "inettmp" serially +DROP INDEX inetidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM inettmp WHERE a < '89.225.196.191'::inet; count diff --git a/contrib/btree_gist/expected/int2.out b/contrib/btree_gist/expected/int2.out index 50a332939b..c695767bd2 100644 --- a/contrib/btree_gist/expected/int2.out +++ b/contrib/btree_gist/expected/int2.out @@ -40,7 +40,14 @@ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); +DEBUG: building index "int2idx" on table "int2tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int2idx_b" on table "int2tmp" serially +DROP INDEX int2idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int2tmp WHERE a < 237::int2; count diff --git a/contrib/btree_gist/expected/int4.out b/contrib/btree_gist/expected/int4.out index 6bbdc7c3f4..3a45937ca1 100644 --- a/contrib/btree_gist/expected/int4.out +++ b/contrib/btree_gist/expected/int4.out @@ -40,7 +40,14 @@ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); +DEBUG: building index "int4idx" on table "int4tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int4idx_b" on table "int4tmp" serially +DROP INDEX int4idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int4tmp WHERE a < 237::int4; count diff --git a/contrib/btree_gist/expected/int8.out b/contrib/btree_gist/expected/int8.out index eff77c26b5..ee7bbae0fc 100644 --- a/contrib/btree_gist/expected/int8.out +++ b/contrib/btree_gist/expected/int8.out @@ -40,7 +40,14 @@ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' 478227196042750 | 13655904687909 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); +DEBUG: building index "int8idx" on table "int8tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int8idx_b" on table "int8tmp" serially +DROP INDEX int8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int8tmp WHERE a < 464571291354841::int8; count diff --git a/contrib/btree_gist/expected/interval.out b/contrib/btree_gist/expected/interval.out index 4c3d494e4a..d720ffbccc 100644 --- a/contrib/btree_gist/expected/interval.out +++ b/contrib/btree_gist/expected/interval.out @@ -40,7 +40,14 @@ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21 @ 220 days 19 hours 5 mins 42 secs | @ 21 days -2 hours -15 mins -41 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); +DEBUG: building index "intervalidx" on table "intervaltmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "intervalidx_b" on table "intervaltmp" serially +DROP INDEX intervalidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM intervaltmp WHERE a < '199 days 21:21:23'::interval; count diff --git a/contrib/btree_gist/expected/macaddr.out b/contrib/btree_gist/expected/macaddr.out index c0a4c6287f..81fe01eaa4 100644 --- a/contrib/btree_gist/expected/macaddr.out +++ b/contrib/btree_gist/expected/macaddr.out @@ -32,7 +32,14 @@ SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); +DEBUG: building index "macaddridx" on table "macaddrtmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "macaddridx_b" on table "macaddrtmp" serially +DROP INDEX macaddridx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM macaddrtmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr; count diff --git a/contrib/btree_gist/expected/macaddr8.out b/contrib/btree_gist/expected/macaddr8.out index e5ec6a5dea..30afd94380 100644 --- a/contrib/btree_gist/expected/macaddr8.out +++ b/contrib/btree_gist/expected/macaddr8.out @@ -32,7 +32,14 @@ SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); +DEBUG: building index "macaddr8idx" on table "macaddr8tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "macaddr8idx_b" on table "macaddr8tmp" serially +DROP INDEX macaddr8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM macaddr8tmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr8; count diff --git a/contrib/btree_gist/expected/numeric.out b/contrib/btree_gist/expected/numeric.out index ae839b8ec8..0b0d491a40 100644 --- a/contrib/btree_gist/expected/numeric.out +++ b/contrib/btree_gist/expected/numeric.out @@ -94,7 +94,14 @@ SELECT count(*) FROM numerictmp WHERE a > 0 ; 576 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); +DEBUG: building index "numericidx" on table "numerictmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "numericidx_b" on table "numerictmp" serially +DROP INDEX numericidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM numerictmp WHERE a < -1890.0; count diff --git a/contrib/btree_gist/expected/oid.out b/contrib/btree_gist/expected/oid.out index 776bbb1026..3c8746483f 100644 --- a/contrib/btree_gist/expected/oid.out +++ b/contrib/btree_gist/expected/oid.out @@ -32,7 +32,14 @@ SELECT count(*) FROM oidtmp WHERE oid > 17; 983 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); +DEBUG: building index "oididx" on table "oidtmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); +DEBUG: building index "oididx_b" on table "oidtmp" serially +DROP INDEX oididx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM oidtmp WHERE oid < 17; count diff --git a/contrib/btree_gist/expected/text.out b/contrib/btree_gist/expected/text.out index bb4e2e62d1..4f17ac413f 100644 --- a/contrib/btree_gist/expected/text.out +++ b/contrib/btree_gist/expected/text.out @@ -33,7 +33,14 @@ SELECT count(*) FROM texttmp WHERE a > '31b0'; 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); +DEBUG: building index "textidx" on table "texttmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "textidx_b" on table "texttmp" serially +DROP INDEX textidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM texttmp WHERE a < '31b0'::text; count diff --git a/contrib/btree_gist/expected/time.out b/contrib/btree_gist/expected/time.out index ec95ef77c5..a71dfc980a 100644 --- a/contrib/btree_gist/expected/time.out +++ b/contrib/btree_gist/expected/time.out @@ -40,7 +40,14 @@ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; 10:55:32 | @ 1 min 39 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); +DEBUG: building index "timeidx" on table "timetmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timeidx_b" on table "timetmp" serially +DROP INDEX timeidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timetmp WHERE a < '10:57:11'::time; count diff --git a/contrib/btree_gist/expected/timestamp.out b/contrib/btree_gist/expected/timestamp.out index 0d94f2f245..b96d5ade8c 100644 --- a/contrib/btree_gist/expected/timestamp.out +++ b/contrib/btree_gist/expected/timestamp.out @@ -40,7 +40,14 @@ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10- Mon Nov 29 20:12:43 2004 | @ 34 days 11 hours 17 mins 35 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); +DEBUG: building index "timestampidx" on table "timestamptmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timestampidx_b" on table "timestamptmp" serially +DROP INDEX timestampidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timestamptmp WHERE a < '2004-10-26 08:55:08'::timestamp; count diff --git a/contrib/btree_gist/expected/timestamptz.out b/contrib/btree_gist/expected/timestamptz.out index 75a15a4256..72f466fd7c 100644 --- a/contrib/btree_gist/expected/timestamptz.out +++ b/contrib/btree_gist/expected/timestamptz.out @@ -100,7 +100,14 @@ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> ' Thu Jan 24 12:28:12 2019 PST | @ 37 days 7 hours 28 mins 18 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); +DEBUG: building index "timestamptzidx" on table "timestamptztmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timestamptzidx_b" on table "timestamptztmp" serially +DROP INDEX timestamptzidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timestamptztmp WHERE a < '2018-12-18 10:59:54 GMT+3'::timestamptz; count diff --git a/contrib/btree_gist/expected/timetz.out b/contrib/btree_gist/expected/timetz.out index 7f73e44797..2eb70af8b1 100644 --- a/contrib/btree_gist/expected/timetz.out +++ b/contrib/btree_gist/expected/timetz.out @@ -18,7 +18,14 @@ INSERT INTO timetzcmp (r_id,a) SELECT 22,count(*) FROM timetztmp WHERE a <= '07: INSERT INTO timetzcmp (r_id,a) SELECT 23,count(*) FROM timetztmp WHERE a = '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 24,count(*) FROM timetztmp WHERE a >= '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07:46:45 GMT+4'; +SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); +DEBUG: building index "timetzidx" on table "timetztmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timetzidx_b" on table "timetztmp" serially +DROP INDEX timetzidx_b; +RESET client_min_messages; SET enable_seqscan=off; UPDATE timetzcmp SET b=c FROM ( SELECT count(*) AS c FROM timetztmp WHERE a < '07:46:45 GMT+3'::timetz ) q WHERE r_id=1 ; UPDATE timetzcmp SET b=c FROM ( SELECT count(*) AS c FROM timetztmp WHERE a <= '07:46:45 GMT+3'::timetz ) q WHERE r_id=2 ; diff --git a/contrib/btree_gist/expected/uuid.out b/contrib/btree_gist/expected/uuid.out index a34b024603..112d70055a 100644 --- a/contrib/btree_gist/expected/uuid.out +++ b/contrib/btree_gist/expected/uuid.out @@ -32,7 +32,14 @@ SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; 375 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); +DEBUG: building index "uuididx" on table "uuidtmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "uuididx_b" on table "uuidtmp" serially +DROP INDEX uuididx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; count diff --git a/contrib/btree_gist/expected/varbit.out b/contrib/btree_gist/expected/varbit.out index ede36bc3ea..d64d0ee6b4 100644 --- a/contrib/btree_gist/expected/varbit.out +++ b/contrib/btree_gist/expected/varbit.out @@ -32,7 +32,14 @@ SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; 50 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); +DEBUG: building index "varbitidx" on table "varbittmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "varbitidx_b" on table "varbittmp" serially +DROP INDEX varbitidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM varbittmp WHERE a < '1110100111010'::varbit; count diff --git a/contrib/btree_gist/expected/varchar.out b/contrib/btree_gist/expected/varchar.out index d071d714cd..9f4f95616a 100644 --- a/contrib/btree_gist/expected/varchar.out +++ b/contrib/btree_gist/expected/varchar.out @@ -32,7 +32,14 @@ SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); +DEBUG: building index "vcharidx" on table "vchartmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); +DEBUG: building index "vcharidx_b" on table "vchartmp" serially +DROP INDEX vcharidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM vchartmp WHERE a < '31b0'::varchar(32); count diff --git a/contrib/btree_gist/sql/bit.sql b/contrib/btree_gist/sql/bit.sql index a733042023..53c67cf77a 100644 --- a/contrib/btree_gist/sql/bit.sql +++ b/contrib/btree_gist/sql/bit.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM bittmp WHERE a >= '011011000100010111011000110000100'; SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; +SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); +CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX bitidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/bytea.sql b/contrib/btree_gist/sql/bytea.sql index 6885f5e56d..fdfa0c345b 100644 --- a/contrib/btree_gist/sql/bytea.sql +++ b/contrib/btree_gist/sql/bytea.sql @@ -17,7 +17,11 @@ SELECT count(*) FROM byteatmp WHERE a >= '31b0'; SELECT count(*) FROM byteatmp WHERE a > '31b0'; +SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); +CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX byteaidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/cash.sql b/contrib/btree_gist/sql/cash.sql index 4526cc4f0a..0581b3593e 100644 --- a/contrib/btree_gist/sql/cash.sql +++ b/contrib/btree_gist/sql/cash.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM moneytmp WHERE a > '22649.64'; SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); +CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); +DROP INDEX moneyidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/char.sql b/contrib/btree_gist/sql/char.sql index f6eb52e672..234eabee3b 100644 --- a/contrib/btree_gist/sql/char.sql +++ b/contrib/btree_gist/sql/char.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM chartmp WHERE a >= '31b0'::char(32); SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); +SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); +CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX charidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/cidr.sql b/contrib/btree_gist/sql/cidr.sql index 9bd77185b9..be2d22b079 100644 --- a/contrib/btree_gist/sql/cidr.sql +++ b/contrib/btree_gist/sql/cidr.sql @@ -15,7 +15,11 @@ SELECT count(*) FROM cidrtmp WHERE a >= '121.111.63.82'; SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; +SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); +CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX cidridx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/date.sql b/contrib/btree_gist/sql/date.sql index f969ef0a08..f007402bac 100644 --- a/contrib/btree_gist/sql/date.sql +++ b/contrib/btree_gist/sql/date.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM datetmp WHERE a > '2001-02-13'; SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); +CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); +DROP INDEX dateidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/enum.sql b/contrib/btree_gist/sql/enum.sql index 476211e979..d6dbcb4239 100644 --- a/contrib/btree_gist/sql/enum.sql +++ b/contrib/btree_gist/sql/enum.sql @@ -20,7 +20,11 @@ SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; +SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); +CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX enumidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/float4.sql b/contrib/btree_gist/sql/float4.sql index 3da1ce953c..0e3eb49343 100644 --- a/contrib/btree_gist/sql/float4.sql +++ b/contrib/btree_gist/sql/float4.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM float4tmp WHERE a > -179.0; SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); +CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX float4idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/float8.sql b/contrib/btree_gist/sql/float8.sql index e1e819b37f..6a216dd606 100644 --- a/contrib/btree_gist/sql/float8.sql +++ b/contrib/btree_gist/sql/float8.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM float8tmp WHERE a > -1890.0; SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); +CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX float8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/inet.sql b/contrib/btree_gist/sql/inet.sql index 4b8d354b00..0339c853d3 100644 --- a/contrib/btree_gist/sql/inet.sql +++ b/contrib/btree_gist/sql/inet.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM inettmp WHERE a >= '89.225.196.191'; SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; +SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); +CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); +DROP INDEX inetidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int2.sql b/contrib/btree_gist/sql/int2.sql index 988518795f..bf98ac65f8 100644 --- a/contrib/btree_gist/sql/int2.sql +++ b/contrib/btree_gist/sql/int2.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int2tmp WHERE a > 237; SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); +CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int2idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int4.sql b/contrib/btree_gist/sql/int4.sql index 659ab5ee24..214993314a 100644 --- a/contrib/btree_gist/sql/int4.sql +++ b/contrib/btree_gist/sql/int4.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int4tmp WHERE a > 237; SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); +CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int4idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int8.sql b/contrib/btree_gist/sql/int8.sql index 51e55e9c14..8a6c2a4bfd 100644 --- a/contrib/btree_gist/sql/int8.sql +++ b/contrib/btree_gist/sql/int8.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int8tmp WHERE a > 464571291354841; SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); +CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/interval.sql b/contrib/btree_gist/sql/interval.sql index 346d6adcb5..6f9b1d4a39 100644 --- a/contrib/btree_gist/sql/interval.sql +++ b/contrib/btree_gist/sql/interval.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM intervaltmp WHERE a > '199 days 21:21:23'; SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); +CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); +DROP INDEX intervalidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/macaddr.sql b/contrib/btree_gist/sql/macaddr.sql index 85c271f7ce..bccfc820ca 100644 --- a/contrib/btree_gist/sql/macaddr.sql +++ b/contrib/btree_gist/sql/macaddr.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM macaddrtmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; +SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); +CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX macaddridx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/macaddr8.sql b/contrib/btree_gist/sql/macaddr8.sql index 61e7d7af40..2d0447a777 100644 --- a/contrib/btree_gist/sql/macaddr8.sql +++ b/contrib/btree_gist/sql/macaddr8.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM macaddr8tmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; +SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); +CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX macaddr8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/numeric.sql b/contrib/btree_gist/sql/numeric.sql index dbb2f2f183..55ecbcdadc 100644 --- a/contrib/btree_gist/sql/numeric.sql +++ b/contrib/btree_gist/sql/numeric.sql @@ -40,7 +40,11 @@ SELECT count(*) FROM numerictmp WHERE a >= 0 ; SELECT count(*) FROM numerictmp WHERE a > 0 ; +SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); +CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); +DROP INDEX numericidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/oid.sql b/contrib/btree_gist/sql/oid.sql index c9358234ce..bc9ee0cba3 100644 --- a/contrib/btree_gist/sql/oid.sql +++ b/contrib/btree_gist/sql/oid.sql @@ -15,7 +15,11 @@ SELECT count(*) FROM oidtmp WHERE oid >= 17; SELECT count(*) FROM oidtmp WHERE oid > 17; +SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); +CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); +DROP INDEX oididx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/text.sql b/contrib/btree_gist/sql/text.sql index 46597e731d..52705a216d 100644 --- a/contrib/btree_gist/sql/text.sql +++ b/contrib/btree_gist/sql/text.sql @@ -17,7 +17,11 @@ SELECT count(*) FROM texttmp WHERE a >= '31b0'; SELECT count(*) FROM texttmp WHERE a > '31b0'; +SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); +CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX textidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/time.sql b/contrib/btree_gist/sql/time.sql index 6104e7f61c..6123945213 100644 --- a/contrib/btree_gist/sql/time.sql +++ b/contrib/btree_gist/sql/time.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM timetmp WHERE a > '10:57:11'; SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); +CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timeidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timestamp.sql b/contrib/btree_gist/sql/timestamp.sql index 95effebfc4..66a14f5ae5 100644 --- a/contrib/btree_gist/sql/timestamp.sql +++ b/contrib/btree_gist/sql/timestamp.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM timestamptmp WHERE a > '2004-10-26 08:55:08'; SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); +CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timestampidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timestamptz.sql b/contrib/btree_gist/sql/timestamptz.sql index f70caa4a64..2a92f63fc4 100644 --- a/contrib/btree_gist/sql/timestamptz.sql +++ b/contrib/btree_gist/sql/timestamptz.sql @@ -39,7 +39,11 @@ SELECT count(*) FROM timestamptztmp WHERE a > '2018-12-18 10:59:54 GMT+4'; SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); +CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timestamptzidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timetz.sql b/contrib/btree_gist/sql/timetz.sql index 2fb725db74..bc79d134b8 100644 --- a/contrib/btree_gist/sql/timetz.sql +++ b/contrib/btree_gist/sql/timetz.sql @@ -42,7 +42,11 @@ INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07: +SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); +CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timetzidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/uuid.sql b/contrib/btree_gist/sql/uuid.sql index 3f7ad764e2..7771bc0d82 100644 --- a/contrib/btree_gist/sql/uuid.sql +++ b/contrib/btree_gist/sql/uuid.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; +SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); +CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX uuididx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/varbit.sql b/contrib/btree_gist/sql/varbit.sql index e2a33b5a1b..6d8243572b 100644 --- a/contrib/btree_gist/sql/varbit.sql +++ b/contrib/btree_gist/sql/varbit.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM varbittmp WHERE a >= '1110100111010'; SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; +SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); +CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX varbitidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/varchar.sql b/contrib/btree_gist/sql/varchar.sql index 8087a17704..59b77e0983 100644 --- a/contrib/btree_gist/sql/varchar.sql +++ b/contrib/btree_gist/sql/varchar.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM vchartmp WHERE a >= '31b0'::varchar(32); SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); +SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); +CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); +DROP INDEX vcharidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index 9d3fa9c3b7..a1950bfc1e 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -257,6 +257,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) if (buildstate.buildMode == GIST_SORTED_BUILD) { + elog(DEBUG1, "Using sorted GiST build"); /* * Sort all data, build the index from bottom up. */ -- 2.24.3 (Apple Git-128) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-12-07 10:05  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  1 sibling, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2020-12-07 10:05 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 28 сент. 2020 г., в 13:12, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> I wrote a couple of 'pageinspect' function to inspect GiST pages for this. See attached.
> <0001-Add-functions-to-pageinspect-to-inspect-GiST-indexes.patch>

Here's version with tests and docs. I still have no idea how to print some useful information about tuples keys.

Thanks!

Best regards, Andrey Borodin.


Attachments:

v2-0001-Add-functions-to-pageinspect-to-inspect-GiST-inde.patch (application/octet-stream, 13.8K) download From 078d759299ab76f1650309ffa4416f0c084be328 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas <heikki.linnakangas@iki.fi> Date: Mon, 28 Sep 2020 11:01:45 +0300 Subject: [PATCH v2] Add functions to 'pageinspect' to inspect GiST indexes. --- contrib/pageinspect/Makefile | 6 +- contrib/pageinspect/expected/gist.out | 140 ++++++++++++++ contrib/pageinspect/gistfuncs.c | 173 ++++++++++++++++++ contrib/pageinspect/pageinspect--1.8--1.9.sql | 27 +++ contrib/pageinspect/pageinspect.control | 2 +- contrib/pageinspect/sql/gist.sql | 15 ++ doc/src/sgml/pageinspect.sgml | 60 ++++++ 7 files changed, 420 insertions(+), 3 deletions(-) create mode 100644 contrib/pageinspect/expected/gist.out create mode 100644 contrib/pageinspect/gistfuncs.c create mode 100644 contrib/pageinspect/pageinspect--1.8--1.9.sql create mode 100644 contrib/pageinspect/sql/gist.sql diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index d9d8177116..4539f0aef7 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -7,19 +7,21 @@ OBJS = \ btreefuncs.o \ fsmfuncs.o \ ginfuncs.o \ + gistfuncs.o \ hashfuncs.o \ heapfuncs.o \ rawpage.o EXTENSION = pageinspect -DATA = pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \ +DATA = pageinspect--1.8--1.9.sql \ + pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \ pageinspect--1.5.sql pageinspect--1.5--1.6.sql \ pageinspect--1.4--1.5.sql pageinspect--1.3--1.4.sql \ pageinspect--1.2--1.3.sql pageinspect--1.1--1.2.sql \ pageinspect--1.0--1.1.sql PGFILEDESC = "pageinspect - functions to inspect contents of database pages" -REGRESS = page btree brin gin hash checksum +REGRESS = page btree brin gin gist hash checksum ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/pageinspect/expected/gist.out b/contrib/pageinspect/expected/gist.out new file mode 100644 index 0000000000..9bcb9d402e --- /dev/null +++ b/contrib/pageinspect/expected/gist.out @@ -0,0 +1,140 @@ +CREATE TABLE test_gist AS SELECT point(i,i) p, i::text t FROM + generate_series(1,1000) i; +CREATE INDEX test_gist_idx ON test_gist USING gist (p); +\x +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 0)); +-[ RECORD 1 ]--------- +lsn | 0/1 +nsn | 0/0 +rightlink | 4294967295 +flags | {} + +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 1)); +-[ RECORD 1 ]--------- +lsn | 0/1 +nsn | 0/0 +rightlink | 4294967295 +flags | {leaf} + +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2)); +-[ RECORD 1 ]----- +lsn | 0/1 +nsn | 0/0 +rightlink | 1 +flags | {leaf} + +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 0)); +-[ RECORD 1 ]--------- +itemoffset | 1 +ctid | (1,65535) +itemlen | 40 +-[ RECORD 2 ]--------- +itemoffset | 2 +ctid | (2,65535) +itemlen | 40 +-[ RECORD 3 ]--------- +itemoffset | 3 +ctid | (3,65535) +itemlen | 40 +-[ RECORD 4 ]--------- +itemoffset | 4 +ctid | (4,65535) +itemlen | 40 +-[ RECORD 5 ]--------- +itemoffset | 5 +ctid | (5,65535) +itemlen | 40 +-[ RECORD 6 ]--------- +itemoffset | 6 +ctid | (6,65535) +itemlen | 40 +-[ RECORD 7 ]--------- +itemoffset | 7 +ctid | (7,65535) +itemlen | 40 + +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 1)) LIMIT 10; +-[ RECORD 1 ]------ +itemoffset | 1 +ctid | (0,1) +itemlen | 40 +-[ RECORD 2 ]------ +itemoffset | 2 +ctid | (0,2) +itemlen | 40 +-[ RECORD 3 ]------ +itemoffset | 3 +ctid | (0,3) +itemlen | 40 +-[ RECORD 4 ]------ +itemoffset | 4 +ctid | (0,4) +itemlen | 40 +-[ RECORD 5 ]------ +itemoffset | 5 +ctid | (0,5) +itemlen | 40 +-[ RECORD 6 ]------ +itemoffset | 6 +ctid | (0,6) +itemlen | 40 +-[ RECORD 7 ]------ +itemoffset | 7 +ctid | (0,7) +itemlen | 40 +-[ RECORD 8 ]------ +itemoffset | 8 +ctid | (0,8) +itemlen | 40 +-[ RECORD 9 ]------ +itemoffset | 9 +ctid | (0,9) +itemlen | 40 +-[ RECORD 10 ]----- +itemoffset | 10 +ctid | (0,10) +itemlen | 40 + +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 2)) LIMIT 10; +-[ RECORD 1 ]------ +itemoffset | 1 +ctid | (1,10) +itemlen | 40 +-[ RECORD 2 ]------ +itemoffset | 2 +ctid | (1,11) +itemlen | 40 +-[ RECORD 3 ]------ +itemoffset | 3 +ctid | (1,12) +itemlen | 40 +-[ RECORD 4 ]------ +itemoffset | 4 +ctid | (1,13) +itemlen | 40 +-[ RECORD 5 ]------ +itemoffset | 5 +ctid | (1,14) +itemlen | 40 +-[ RECORD 6 ]------ +itemoffset | 6 +ctid | (1,15) +itemlen | 40 +-[ RECORD 7 ]------ +itemoffset | 7 +ctid | (1,16) +itemlen | 40 +-[ RECORD 8 ]------ +itemoffset | 8 +ctid | (1,17) +itemlen | 40 +-[ RECORD 9 ]------ +itemoffset | 9 +ctid | (1,18) +itemlen | 40 +-[ RECORD 10 ]----- +itemoffset | 10 +ctid | (1,19) +itemlen | 40 + +DROP TABLE test1; diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c new file mode 100644 index 0000000000..8517b4c241 --- /dev/null +++ b/contrib/pageinspect/gistfuncs.c @@ -0,0 +1,173 @@ +/* + * gistfuncs.c + * Functions to investigate the content of GiST indexes + * + * Copyright (c) 2014-2020, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/pageinspect/gitfuncs.c + */ +#include "postgres.h" + +#include "access/gist.h" +#include "access/htup.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "pageinspect.h" +#include "storage/itemptr.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/pg_lsn.h" + +PG_FUNCTION_INFO_V1(gist_page_opaque_info); +PG_FUNCTION_INFO_V1(gist_page_items); + +#define ItemPointerGetDatum(X) PointerGetDatum(X) + + +Datum +gist_page_opaque_info(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + TupleDesc tupdesc; + Page page; + GISTPageOpaque opaq; + HeapTuple resultTuple; + Datum values[4]; + bool nulls[4]; + Datum flags[16]; + int nflags = 0; + uint16 flagbits; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + page = get_page_from_raw(raw_page); + + opaq = (GISTPageOpaque) PageGetSpecialPointer(page); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Convert the flags bitmask to an array of human-readable names */ + flagbits = opaq->flags; + if (flagbits & F_LEAF) + flags[nflags++] = CStringGetTextDatum("leaf"); + if (flagbits & F_DELETED) + flags[nflags++] = CStringGetTextDatum("deleted"); + if (flagbits & F_TUPLES_DELETED) + flags[nflags++] = CStringGetTextDatum("tuples_deleted"); + if (flagbits & F_FOLLOW_RIGHT) + flags[nflags++] = CStringGetTextDatum("follow_right"); + if (flagbits & F_HAS_GARBAGE) + flags[nflags++] = CStringGetTextDatum("has_garbage"); + flagbits &= ~(F_LEAF | F_DELETED | F_TUPLES_DELETED | F_FOLLOW_RIGHT | F_HAS_GARBAGE); + if (flagbits) + { + /* any flags we don't recognize are printed in hex */ + flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits)); + } + + memset(nulls, 0, sizeof(nulls)); + + values[0] = LSNGetDatum(PageGetLSN(page)); + values[1] = LSNGetDatum(GistPageGetNSN(page)); + values[2] = Int64GetDatum(opaq->rightlink); + values[3] = PointerGetDatum(construct_array(flags, nflags, + TEXTOID, + -1, false, TYPALIGN_INT)); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(tupdesc, values, nulls); + + return HeapTupleGetDatum(resultTuple); +} + +typedef struct gist_page_items_state +{ + Page page; + TupleDesc tupd; + OffsetNumber offset; +} gist_page_items_state; + +Datum +gist_page_items(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + FuncCallContext *fctx; + gist_page_items_state *inter_call_data; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + if (SRF_IS_FIRSTCALL()) + { + TupleDesc tupdesc; + MemoryContext mctx; + Page page; + + fctx = SRF_FIRSTCALL_INIT(); + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + page = get_page_from_raw(raw_page); + + inter_call_data = palloc(sizeof(gist_page_items_state)); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + inter_call_data->page = page; + inter_call_data->tupd = tupdesc; + inter_call_data->offset = FirstOffsetNumber; + + fctx->max_calls = PageGetMaxOffsetNumber(page); + fctx->user_fctx = inter_call_data; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + inter_call_data = fctx->user_fctx; + + if (fctx->call_cntr < fctx->max_calls) + { + Page page = inter_call_data->page; + OffsetNumber offset = inter_call_data->offset; + HeapTuple resultTuple; + Datum result; + Datum values[3]; + bool nulls[3]; + ItemId id; + IndexTuple itup; + + id = PageGetItemId(page, offset); + + if (!ItemIdIsValid(id)) + elog(ERROR, "invalid ItemId"); + + itup = (IndexTuple) PageGetItem(page, id); + + memset(nulls, 0, sizeof(nulls)); + + values[0] = DatumGetInt16(offset); + values[1] = ItemPointerGetDatum(&itup->t_tid); + values[2] = Int32GetDatum((int) IndexTupleSize(itup)); + + /* TODO: also print the keys */ + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + inter_call_data->offset++; + SRF_RETURN_NEXT(fctx, result); + } + + SRF_RETURN_DONE(fctx); +} diff --git a/contrib/pageinspect/pageinspect--1.8--1.9.sql b/contrib/pageinspect/pageinspect--1.8--1.9.sql new file mode 100644 index 0000000000..e7a849cfed --- /dev/null +++ b/contrib/pageinspect/pageinspect--1.8--1.9.sql @@ -0,0 +1,27 @@ +/* contrib/pageinspect/pageinspect--1.8--1.9.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.9'" to load this file. \quit + +-- +-- gist_page_opaque_info() +-- +CREATE FUNCTION gist_page_opaque_info(IN page bytea, + OUT lsn pg_lsn, + OUT nsn pg_lsn, + OUT rightlink bigint, + OUT flags text[]) +AS 'MODULE_PATHNAME', 'gist_page_opaque_info' +LANGUAGE C STRICT PARALLEL SAFE; + + +-- +-- gist_page_items() +-- +CREATE FUNCTION gist_page_items(IN page bytea, + OUT itemoffset smallint, + OUT ctid tid, + OUT itemlen smallint) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'gist_page_items' +LANGUAGE C STRICT PARALLEL SAFE; diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control index f8cdf526c6..bd716769a1 100644 --- a/contrib/pageinspect/pageinspect.control +++ b/contrib/pageinspect/pageinspect.control @@ -1,5 +1,5 @@ # pageinspect extension comment = 'inspect the contents of database pages at a low level' -default_version = '1.8' +default_version = '1.9' module_pathname = '$libdir/pageinspect' relocatable = true diff --git a/contrib/pageinspect/sql/gist.sql b/contrib/pageinspect/sql/gist.sql new file mode 100644 index 0000000000..12be3cd76c --- /dev/null +++ b/contrib/pageinspect/sql/gist.sql @@ -0,0 +1,15 @@ +CREATE TABLE test_gist AS SELECT point(i,i) p, i::text t FROM + generate_series(1,1000) i; +CREATE INDEX test_gist_idx ON test_gist USING gist (p); + +\x + +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 0)); +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 1)); +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2)); + +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 0)); +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 1)) LIMIT 10; +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 2)) LIMIT 10; + +DROP TABLE test1; diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml index 687c3606ba..32cfde3628 100644 --- a/doc/src/sgml/pageinspect.sgml +++ b/doc/src/sgml/pageinspect.sgml @@ -671,6 +671,66 @@ test=# SELECT first_tid, nbytes, tids[0:5] AS some_tids </variablelist> </sect2> + <sect2> + <title>GiST Functions</title> + + <variablelist> + <varlistentry> + <term> + <function>gist_page_opaque_info(page bytea) returns record</function> + <indexterm> + <primary>gist_page_opaque_info</primary> + </indexterm> + </term> + + <listitem> + <para> + <function>gist_page_opaque_info</function> returns information about + a <acronym>GiST</acronym> index opaque area, like the NSN, rightlink and + page type. + For example: +<screen> +test=# SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2)); + lsn | nsn | rightlink | flags +-----+-----+-----------+-------- + 0/1 | 0/0 | 1 | {leaf} +(1 row) +</screen> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <function>gist_page_items(page bytea) returns setof record</function> + <indexterm> + <primary>gist_page_items</primary> + </indexterm> + </term> + + <listitem> + <para> + <function>gist_page_items</function> returns information about + the data stored in a <acronym>GiST</acronym> page. For example: +<screen> +test=# SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 0)); + itemoffset | ctid | itemlen +------------+-----------+--------- + 1 | (1,65535) | 40 + 2 | (2,65535) | 40 + 3 | (3,65535) | 40 + 4 | (4,65535) | 40 + 5 | (5,65535) | 40 + 6 | (6,65535) | 40 + 7 | (7,65535) | 40 +(7 rows) +</screen> + </para> + </listitem> + </varlistentry> + </variablelist> + </sect2> + <sect2> <title>Hash Functions</title> -- 2.24.3 (Apple Git-128) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-12-07 18:56  Peter Geoghegan <pg@bowt.ie>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Peter Geoghegan @ 2020-12-07 18:56 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Mon, Dec 7, 2020 at 2:05 AM Andrey Borodin <x4mmm@yandex-team.ru> wrote:
> Here's version with tests and docs. I still have no idea how to print some useful information about tuples keys.

I suggest calling BuildIndexValueDescription() from your own custom
debug instrumentation code.

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-12-09 09:47  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Peter Geoghegan <pg@bowt.ie>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2020-12-09 09:47 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 7 дек. 2020 г., в 23:56, Peter Geoghegan <pg@bowt.ie> написал(а):
> 
> On Mon, Dec 7, 2020 at 2:05 AM Andrey Borodin <x4mmm@yandex-team.ru> wrote:
>> Here's version with tests and docs. I still have no idea how to print some useful information about tuples keys.
> 
> I suggest calling BuildIndexValueDescription() from your own custom
> debug instrumentation code.
Thanks for the hint, Peter!
This function does exactly what I want to do. But I have no Relation inside gist_page_items(bytea) function... probably, I'll add gist_page_items(relname, blockno) overload to fetch keys.

Thanks!

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2020-12-10 10:16  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2020-12-10 10:16 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 9 дек. 2020 г., в 14:47, Andrey Borodin <x4mmm@yandex-team.ru> написал(а):
> 
> 
> 
>> 7 дек. 2020 г., в 23:56, Peter Geoghegan <pg@bowt.ie> написал(а):
>> 
>> On Mon, Dec 7, 2020 at 2:05 AM Andrey Borodin <x4mmm@yandex-team.ru> wrote:
>>> Here's version with tests and docs. I still have no idea how to print some useful information about tuples keys.
>> 
>> I suggest calling BuildIndexValueDescription() from your own custom
>> debug instrumentation code.
> Thanks for the hint, Peter!
> This function does exactly what I want to do. But I have no Relation inside gist_page_items(bytea) function... probably, I'll add gist_page_items(relname, blockno) overload to fetch keys.

PFA patch with implementation.

Best regards, Andrey Borodin.


Attachments:

v3-0001-Add-functions-to-pageinspect-to-inspect-GiST-inde.patch (application/octet-stream, 28.0K) download From c17c3b7122860bca232d2de3b822e490ca5d066b Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas <heikki.linnakangas@iki.fi> Date: Mon, 28 Sep 2020 11:01:45 +0300 Subject: [PATCH v3] Add functions to 'pageinspect' to inspect GiST indexes. --- contrib/pageinspect/Makefile | 6 +- contrib/pageinspect/expected/gist.out | 308 ++++++++++++++++++ contrib/pageinspect/gistfuncs.c | 305 +++++++++++++++++ contrib/pageinspect/pageinspect--1.8--1.9.sql | 41 +++ contrib/pageinspect/pageinspect.control | 2 +- contrib/pageinspect/pageinspect.h | 5 + contrib/pageinspect/sql/gist.sql | 19 ++ doc/src/sgml/pageinspect.sgml | 89 +++++ 8 files changed, 772 insertions(+), 3 deletions(-) create mode 100644 contrib/pageinspect/expected/gist.out create mode 100644 contrib/pageinspect/gistfuncs.c create mode 100644 contrib/pageinspect/pageinspect--1.8--1.9.sql create mode 100644 contrib/pageinspect/sql/gist.sql diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index d9d8177116..4539f0aef7 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -7,19 +7,21 @@ OBJS = \ btreefuncs.o \ fsmfuncs.o \ ginfuncs.o \ + gistfuncs.o \ hashfuncs.o \ heapfuncs.o \ rawpage.o EXTENSION = pageinspect -DATA = pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \ +DATA = pageinspect--1.8--1.9.sql \ + pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \ pageinspect--1.5.sql pageinspect--1.5--1.6.sql \ pageinspect--1.4--1.5.sql pageinspect--1.3--1.4.sql \ pageinspect--1.2--1.3.sql pageinspect--1.1--1.2.sql \ pageinspect--1.0--1.1.sql PGFILEDESC = "pageinspect - functions to inspect contents of database pages" -REGRESS = page btree brin gin hash checksum +REGRESS = page btree brin gin gist hash checksum ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/pageinspect/expected/gist.out b/contrib/pageinspect/expected/gist.out new file mode 100644 index 0000000000..55bb62268a --- /dev/null +++ b/contrib/pageinspect/expected/gist.out @@ -0,0 +1,308 @@ +CREATE TABLE test_gist AS SELECT point(i,i) p, i::text t FROM + generate_series(1,1000) i; +CREATE INDEX test_gist_idx ON test_gist USING gist (p); +\x +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 0)); +-[ RECORD 1 ]--------- +lsn | 0/1 +nsn | 0/0 +rightlink | 4294967295 +flags | {} + +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 1)); +-[ RECORD 1 ]--------- +lsn | 0/1 +nsn | 0/0 +rightlink | 4294967295 +flags | {leaf} + +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2)); +-[ RECORD 1 ]----- +lsn | 0/1 +nsn | 0/0 +rightlink | 1 +flags | {leaf} + +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 0)); +-[ RECORD 1 ]---------------------------------------------------------------------------------- +itemoffset | 1 +ctid | (1,65535) +itemlen | 40 +tuple_data | \x00000100ffff28000000000000c064400000000000c06440000000000000f03f000000000000f03f +-[ RECORD 2 ]---------------------------------------------------------------------------------- +itemoffset | 2 +ctid | (2,65535) +itemlen | 40 +tuple_data | \x00000200ffff28000000000000c074400000000000c074400000000000e064400000000000e06440 +-[ RECORD 3 ]---------------------------------------------------------------------------------- +itemoffset | 3 +ctid | (3,65535) +itemlen | 40 +tuple_data | \x00000300ffff28000000000000207f400000000000207f400000000000d074400000000000d07440 +-[ RECORD 4 ]---------------------------------------------------------------------------------- +itemoffset | 4 +ctid | (4,65535) +itemlen | 40 +tuple_data | \x00000400ffff28000000000000c084400000000000c084400000000000307f400000000000307f40 +-[ RECORD 5 ]---------------------------------------------------------------------------------- +itemoffset | 5 +ctid | (5,65535) +itemlen | 40 +tuple_data | \x00000500ffff28000000000000f089400000000000f089400000000000c884400000000000c88440 +-[ RECORD 6 ]---------------------------------------------------------------------------------- +itemoffset | 6 +ctid | (6,65535) +itemlen | 40 +tuple_data | \x00000600ffff28000000000000208f400000000000208f400000000000f889400000000000f88940 +-[ RECORD 7 ]---------------------------------------------------------------------------------- +itemoffset | 7 +ctid | (7,65535) +itemlen | 40 +tuple_data | \x00000700ffff28000000000000408f400000000000408f400000000000288f400000000000288f40 + +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 1)) LIMIT 10; +-[ RECORD 1 ]---------------------------------------------------------------------------------- +itemoffset | 1 +ctid | (0,1) +itemlen | 40 +tuple_data | \x0000000001002800000000000000f03f000000000000f03f000000000000f03f000000000000f03f +-[ RECORD 2 ]---------------------------------------------------------------------------------- +itemoffset | 2 +ctid | (0,2) +itemlen | 40 +tuple_data | \x00000000020028000000000000000040000000000000004000000000000000400000000000000040 +-[ RECORD 3 ]---------------------------------------------------------------------------------- +itemoffset | 3 +ctid | (0,3) +itemlen | 40 +tuple_data | \x00000000030028000000000000000840000000000000084000000000000008400000000000000840 +-[ RECORD 4 ]---------------------------------------------------------------------------------- +itemoffset | 4 +ctid | (0,4) +itemlen | 40 +tuple_data | \x00000000040028000000000000001040000000000000104000000000000010400000000000001040 +-[ RECORD 5 ]---------------------------------------------------------------------------------- +itemoffset | 5 +ctid | (0,5) +itemlen | 40 +tuple_data | \x00000000050028000000000000001440000000000000144000000000000014400000000000001440 +-[ RECORD 6 ]---------------------------------------------------------------------------------- +itemoffset | 6 +ctid | (0,6) +itemlen | 40 +tuple_data | \x00000000060028000000000000001840000000000000184000000000000018400000000000001840 +-[ RECORD 7 ]---------------------------------------------------------------------------------- +itemoffset | 7 +ctid | (0,7) +itemlen | 40 +tuple_data | \x00000000070028000000000000001c400000000000001c400000000000001c400000000000001c40 +-[ RECORD 8 ]---------------------------------------------------------------------------------- +itemoffset | 8 +ctid | (0,8) +itemlen | 40 +tuple_data | \x00000000080028000000000000002040000000000000204000000000000020400000000000002040 +-[ RECORD 9 ]---------------------------------------------------------------------------------- +itemoffset | 9 +ctid | (0,9) +itemlen | 40 +tuple_data | \x00000000090028000000000000002240000000000000224000000000000022400000000000002240 +-[ RECORD 10 ]--------------------------------------------------------------------------------- +itemoffset | 10 +ctid | (0,10) +itemlen | 40 +tuple_data | \x000000000a0028000000000000002440000000000000244000000000000024400000000000002440 + +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 2)) LIMIT 10; +-[ RECORD 1 ]---------------------------------------------------------------------------------- +itemoffset | 1 +ctid | (1,10) +itemlen | 40 +tuple_data | \x000001000a0028000000000000e064400000000000e064400000000000e064400000000000e06440 +-[ RECORD 2 ]---------------------------------------------------------------------------------- +itemoffset | 2 +ctid | (1,11) +itemlen | 40 +tuple_data | \x000001000b0028000000000000006540000000000000654000000000000065400000000000006540 +-[ RECORD 3 ]---------------------------------------------------------------------------------- +itemoffset | 3 +ctid | (1,12) +itemlen | 40 +tuple_data | \x000001000c0028000000000000206540000000000020654000000000002065400000000000206540 +-[ RECORD 4 ]---------------------------------------------------------------------------------- +itemoffset | 4 +ctid | (1,13) +itemlen | 40 +tuple_data | \x000001000d0028000000000000406540000000000040654000000000004065400000000000406540 +-[ RECORD 5 ]---------------------------------------------------------------------------------- +itemoffset | 5 +ctid | (1,14) +itemlen | 40 +tuple_data | \x000001000e0028000000000000606540000000000060654000000000006065400000000000606540 +-[ RECORD 6 ]---------------------------------------------------------------------------------- +itemoffset | 6 +ctid | (1,15) +itemlen | 40 +tuple_data | \x000001000f0028000000000000806540000000000080654000000000008065400000000000806540 +-[ RECORD 7 ]---------------------------------------------------------------------------------- +itemoffset | 7 +ctid | (1,16) +itemlen | 40 +tuple_data | \x00000100100028000000000000a065400000000000a065400000000000a065400000000000a06540 +-[ RECORD 8 ]---------------------------------------------------------------------------------- +itemoffset | 8 +ctid | (1,17) +itemlen | 40 +tuple_data | \x00000100110028000000000000c065400000000000c065400000000000c065400000000000c06540 +-[ RECORD 9 ]---------------------------------------------------------------------------------- +itemoffset | 9 +ctid | (1,18) +itemlen | 40 +tuple_data | \x00000100120028000000000000e065400000000000e065400000000000e065400000000000e06540 +-[ RECORD 10 ]--------------------------------------------------------------------------------- +itemoffset | 10 +ctid | (1,19) +itemlen | 40 +tuple_data | \x00000100130028000000000000006640000000000000664000000000000066400000000000006640 + +SELECT * FROM gist_page_items('test_gist_idx', 0); +-[ RECORD 1 ]----------------- +itemoffset | 1 +ctid | (1,65535) +itemlen | 40 +keys | (p)=((166,166)) +-[ RECORD 2 ]----------------- +itemoffset | 2 +ctid | (2,65535) +itemlen | 40 +keys | (p)=((332,332)) +-[ RECORD 3 ]----------------- +itemoffset | 3 +ctid | (3,65535) +itemlen | 40 +keys | (p)=((498,498)) +-[ RECORD 4 ]----------------- +itemoffset | 4 +ctid | (4,65535) +itemlen | 40 +keys | (p)=((664,664)) +-[ RECORD 5 ]----------------- +itemoffset | 5 +ctid | (5,65535) +itemlen | 40 +keys | (p)=((830,830)) +-[ RECORD 6 ]----------------- +itemoffset | 6 +ctid | (6,65535) +itemlen | 40 +keys | (p)=((996,996)) +-[ RECORD 7 ]----------------- +itemoffset | 7 +ctid | (7,65535) +itemlen | 40 +keys | (p)=((1000,1000)) + +SELECT * FROM gist_page_items('test_gist_idx', 1) LIMIT 10; +-[ RECORD 1 ]------------- +itemoffset | 1 +ctid | (0,1) +itemlen | 40 +keys | (p)=((1,1)) +-[ RECORD 2 ]------------- +itemoffset | 2 +ctid | (0,2) +itemlen | 40 +keys | (p)=((2,2)) +-[ RECORD 3 ]------------- +itemoffset | 3 +ctid | (0,3) +itemlen | 40 +keys | (p)=((3,3)) +-[ RECORD 4 ]------------- +itemoffset | 4 +ctid | (0,4) +itemlen | 40 +keys | (p)=((4,4)) +-[ RECORD 5 ]------------- +itemoffset | 5 +ctid | (0,5) +itemlen | 40 +keys | (p)=((5,5)) +-[ RECORD 6 ]------------- +itemoffset | 6 +ctid | (0,6) +itemlen | 40 +keys | (p)=((6,6)) +-[ RECORD 7 ]------------- +itemoffset | 7 +ctid | (0,7) +itemlen | 40 +keys | (p)=((7,7)) +-[ RECORD 8 ]------------- +itemoffset | 8 +ctid | (0,8) +itemlen | 40 +keys | (p)=((8,8)) +-[ RECORD 9 ]------------- +itemoffset | 9 +ctid | (0,9) +itemlen | 40 +keys | (p)=((9,9)) +-[ RECORD 10 ]------------ +itemoffset | 10 +ctid | (0,10) +itemlen | 40 +keys | (p)=((10,10)) + +SELECT * FROM gist_page_items('test_gist_idx', 2) LIMIT 10; +-[ RECORD 1 ]--------------- +itemoffset | 1 +ctid | (1,10) +itemlen | 40 +keys | (p)=((167,167)) +-[ RECORD 2 ]--------------- +itemoffset | 2 +ctid | (1,11) +itemlen | 40 +keys | (p)=((168,168)) +-[ RECORD 3 ]--------------- +itemoffset | 3 +ctid | (1,12) +itemlen | 40 +keys | (p)=((169,169)) +-[ RECORD 4 ]--------------- +itemoffset | 4 +ctid | (1,13) +itemlen | 40 +keys | (p)=((170,170)) +-[ RECORD 5 ]--------------- +itemoffset | 5 +ctid | (1,14) +itemlen | 40 +keys | (p)=((171,171)) +-[ RECORD 6 ]--------------- +itemoffset | 6 +ctid | (1,15) +itemlen | 40 +keys | (p)=((172,172)) +-[ RECORD 7 ]--------------- +itemoffset | 7 +ctid | (1,16) +itemlen | 40 +keys | (p)=((173,173)) +-[ RECORD 8 ]--------------- +itemoffset | 8 +ctid | (1,17) +itemlen | 40 +keys | (p)=((174,174)) +-[ RECORD 9 ]--------------- +itemoffset | 9 +ctid | (1,18) +itemlen | 40 +keys | (p)=((175,175)) +-[ RECORD 10 ]-------------- +itemoffset | 10 +ctid | (1,19) +itemlen | 40 +keys | (p)=((176,176)) + +DROP TABLE test1; diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c new file mode 100644 index 0000000000..9b545364c5 --- /dev/null +++ b/contrib/pageinspect/gistfuncs.c @@ -0,0 +1,305 @@ +/* + * gistfuncs.c + * Functions to investigate the content of GiST indexes + * + * Copyright (c) 2014-2020, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/pageinspect/gitfuncs.c + */ +#include "postgres.h" + +#include "access/gist.h" +#include "access/gist_private.h" +#include "access/htup.h" +#include "access/relation.h" +#include "catalog/namespace.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "pageinspect.h" +#include "storage/itemptr.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/rel.h" +#include "utils/pg_lsn.h" +#include "utils/varlena.h" + +PG_FUNCTION_INFO_V1(gist_page_opaque_info); +PG_FUNCTION_INFO_V1(gist_page_items); +PG_FUNCTION_INFO_V1(gist_page_items_bytea); + +#define ItemPointerGetDatum(X) PointerGetDatum(X) + + +Datum +gist_page_opaque_info(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + TupleDesc tupdesc; + Page page; + GISTPageOpaque opaq; + HeapTuple resultTuple; + Datum values[4]; + bool nulls[4]; + Datum flags[16]; + int nflags = 0; + uint16 flagbits; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + page = get_page_from_raw(raw_page); + + opaq = (GISTPageOpaque) PageGetSpecialPointer(page); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Convert the flags bitmask to an array of human-readable names */ + flagbits = opaq->flags; + if (flagbits & F_LEAF) + flags[nflags++] = CStringGetTextDatum("leaf"); + if (flagbits & F_DELETED) + flags[nflags++] = CStringGetTextDatum("deleted"); + if (flagbits & F_TUPLES_DELETED) + flags[nflags++] = CStringGetTextDatum("tuples_deleted"); + if (flagbits & F_FOLLOW_RIGHT) + flags[nflags++] = CStringGetTextDatum("follow_right"); + if (flagbits & F_HAS_GARBAGE) + flags[nflags++] = CStringGetTextDatum("has_garbage"); + flagbits &= ~(F_LEAF | F_DELETED | F_TUPLES_DELETED | F_FOLLOW_RIGHT | F_HAS_GARBAGE); + if (flagbits) + { + /* any flags we don't recognize are printed in hex */ + flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits)); + } + + memset(nulls, 0, sizeof(nulls)); + + values[0] = LSNGetDatum(PageGetLSN(page)); + values[1] = LSNGetDatum(GistPageGetNSN(page)); + values[2] = Int64GetDatum(opaq->rightlink); + values[3] = PointerGetDatum(construct_array(flags, nflags, + TEXTOID, + -1, false, TYPALIGN_INT)); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(tupdesc, values, nulls); + + return HeapTupleGetDatum(resultTuple); +} + +typedef struct gist_page_items_state +{ + Page page; + TupleDesc tupd; + OffsetNumber offset; + Relation rel; +} gist_page_items_state; + +Datum +gist_page_items_bytea(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + FuncCallContext *fctx; + gist_page_items_state *inter_call_data; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + if (SRF_IS_FIRSTCALL()) + { + TupleDesc tupdesc; + MemoryContext mctx; + Page page; + + fctx = SRF_FIRSTCALL_INIT(); + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + page = get_page_from_raw(raw_page); + + inter_call_data = palloc(sizeof(gist_page_items_state)); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + if (GistPageIsDeleted(page)) + elog(NOTICE, "page is deleted"); + + inter_call_data->page = page; + inter_call_data->tupd = tupdesc; + inter_call_data->offset = FirstOffsetNumber; + + fctx->max_calls = PageGetMaxOffsetNumber(page); + fctx->user_fctx = inter_call_data; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + inter_call_data = fctx->user_fctx; + + if (fctx->call_cntr < fctx->max_calls) + { + Page page = inter_call_data->page; + OffsetNumber offset = inter_call_data->offset; + HeapTuple resultTuple; + Datum result; + Datum values[4]; + bool nulls[4]; + ItemId id; + IndexTuple itup; + bytea *tuple_bytea; + int tuple_len; + + id = PageGetItemId(page, offset); + + if (!ItemIdIsValid(id)) + elog(ERROR, "invalid ItemId"); + + itup = (IndexTuple) PageGetItem(page, id); + tuple_len = IndexTupleSize(itup); + + memset(nulls, 0, sizeof(nulls)); + + values[0] = DatumGetInt16(offset); + values[1] = ItemPointerGetDatum(&itup->t_tid); + values[2] = Int32GetDatum((int) IndexTupleSize(itup)); + + tuple_bytea = (bytea *) palloc(tuple_len + VARHDRSZ); + SET_VARSIZE(tuple_bytea, tuple_len + VARHDRSZ); + memcpy(VARDATA(tuple_bytea), itup, tuple_len); + values[3] = PointerGetDatum(tuple_bytea); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + inter_call_data->offset++; + SRF_RETURN_NEXT(fctx, result); + } + + SRF_RETURN_DONE(fctx); +} + +Datum +gist_page_items(PG_FUNCTION_ARGS) +{ + text *relname = PG_GETARG_TEXT_PP(0); + uint32 blkno = PG_GETARG_UINT32(1); + FuncCallContext *fctx; + gist_page_items_state *inter_call_data; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + if (SRF_IS_FIRSTCALL()) + { + RangeVar *relrv; + Relation rel; + TupleDesc tupdesc; + MemoryContext mctx; + Page page; + Buffer buffer; + + fctx = SRF_FIRSTCALL_INIT(); + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + CHECK_RELATION_BLOCK_RANGE(rel, blkno); + + buffer = ReadBuffer(rel, blkno); + LockBuffer(buffer, GIST_SHARE); + + + inter_call_data = palloc(sizeof(gist_page_items_state)); + + /* + * We copy the page into local storage to avoid holding pin on the + * buffer longer than we must, and possibly failing to release it at + * all if the calling query doesn't fetch all rows. + */ + page = palloc(BLCKSZ); + memcpy(page, BufferGetPage(buffer), BLCKSZ); + + UnlockReleaseBuffer(buffer); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + if (GistPageIsDeleted(page)) + elog(NOTICE, "page is deleted"); + + inter_call_data->page = page; + inter_call_data->tupd = tupdesc; + inter_call_data->offset = FirstOffsetNumber; + inter_call_data->rel = rel; + + fctx->max_calls = PageGetMaxOffsetNumber(page); + fctx->user_fctx = inter_call_data; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + inter_call_data = fctx->user_fctx; + + if (fctx->call_cntr < fctx->max_calls) + { + Page page = inter_call_data->page; + OffsetNumber offset = inter_call_data->offset; + HeapTuple resultTuple; + Datum result; + Datum values[4]; + bool nulls[4]; + ItemId id; + IndexTuple itup; + + Datum itup_values[INDEX_MAX_KEYS]; + bool itup_isnull[INDEX_MAX_KEYS]; + char *key_desc; + + id = PageGetItemId(page, offset); + + if (!ItemIdIsValid(id)) + elog(ERROR, "invalid ItemId"); + + itup = (IndexTuple) PageGetItem(page, id); + + index_deform_tuple(itup, RelationGetDescr(inter_call_data->rel), + itup_values, itup_isnull); + + key_desc = BuildIndexValueDescription(inter_call_data->rel, itup_values, + itup_isnull); + + memset(nulls, 0, sizeof(nulls)); + + values[0] = DatumGetInt16(offset); + values[1] = ItemPointerGetDatum(&itup->t_tid); + values[2] = Int32GetDatum((int) IndexTupleSize(itup)); + values[3] = CStringGetTextDatum(key_desc); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + inter_call_data->offset++; + SRF_RETURN_NEXT(fctx, result); + } + + relation_close(inter_call_data->rel, AccessShareLock); + + SRF_RETURN_DONE(fctx); +} \ No newline at end of file diff --git a/contrib/pageinspect/pageinspect--1.8--1.9.sql b/contrib/pageinspect/pageinspect--1.8--1.9.sql new file mode 100644 index 0000000000..454b5707ec --- /dev/null +++ b/contrib/pageinspect/pageinspect--1.8--1.9.sql @@ -0,0 +1,41 @@ +/* contrib/pageinspect/pageinspect--1.8--1.9.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.9'" to load this file. \quit + +-- +-- gist_page_opaque_info() +-- +CREATE FUNCTION gist_page_opaque_info(IN page bytea, + OUT lsn pg_lsn, + OUT nsn pg_lsn, + OUT rightlink bigint, + OUT flags text[]) +AS 'MODULE_PATHNAME', 'gist_page_opaque_info' +LANGUAGE C STRICT PARALLEL SAFE; + + +-- +-- gist_page_items_bytea() +-- +CREATE FUNCTION gist_page_items_bytea(IN page bytea, + OUT itemoffset smallint, + OUT ctid tid, + OUT itemlen smallint, + OUT tuple_data bytea) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'gist_page_items_bytea' +LANGUAGE C STRICT PARALLEL SAFE; + +-- +-- gist_page_items() +-- +CREATE FUNCTION gist_page_items(IN page bytea, + IN blockno int4, + OUT itemoffset smallint, + OUT ctid tid, + OUT itemlen smallint, + OUT keys text) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'gist_page_items' +LANGUAGE C STRICT PARALLEL SAFE; diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control index f8cdf526c6..bd716769a1 100644 --- a/contrib/pageinspect/pageinspect.control +++ b/contrib/pageinspect/pageinspect.control @@ -1,5 +1,5 @@ # pageinspect extension comment = 'inspect the contents of database pages at a low level' -default_version = '1.8' +default_version = '1.9' module_pathname = '$libdir/pageinspect' relocatable = true diff --git a/contrib/pageinspect/pageinspect.h b/contrib/pageinspect/pageinspect.h index 478e0d2d20..2ba9da1cd8 100644 --- a/contrib/pageinspect/pageinspect.h +++ b/contrib/pageinspect/pageinspect.h @@ -18,4 +18,9 @@ /* in rawpage.c */ extern Page get_page_from_raw(bytea *raw_page); +/* note: BlockNumber is unsigned, hence can't be negative */ +#define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \ + if ( RelationGetNumberOfBlocks(rel) <= (BlockNumber) (blkno) ) \ + elog(ERROR, "block number out of range"); } + #endif /* _PAGEINSPECT_H_ */ diff --git a/contrib/pageinspect/sql/gist.sql b/contrib/pageinspect/sql/gist.sql new file mode 100644 index 0000000000..24f4e73fbe --- /dev/null +++ b/contrib/pageinspect/sql/gist.sql @@ -0,0 +1,19 @@ +CREATE TABLE test_gist AS SELECT point(i,i) p, i::text t FROM + generate_series(1,1000) i; +CREATE INDEX test_gist_idx ON test_gist USING gist (p); + +\x + +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 0)); +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 1)); +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2)); + +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 0)); +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 1)) LIMIT 10; +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 2)) LIMIT 10; + +SELECT * FROM gist_page_items('test_gist_idx', 0); +SELECT * FROM gist_page_items('test_gist_idx', 1) LIMIT 10; +SELECT * FROM gist_page_items('test_gist_idx', 2) LIMIT 10; + +DROP TABLE test1; diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml index 687c3606ba..c411fcae5c 100644 --- a/doc/src/sgml/pageinspect.sgml +++ b/doc/src/sgml/pageinspect.sgml @@ -671,6 +671,95 @@ test=# SELECT first_tid, nbytes, tids[0:5] AS some_tids </variablelist> </sect2> + <sect2> + <title>GiST Functions</title> + + <variablelist> + <varlistentry> + <term> + <function>gist_page_opaque_info(page bytea) returns record</function> + <indexterm> + <primary>gist_page_opaque_info</primary> + </indexterm> + </term> + + <listitem> + <para> + <function>gist_page_opaque_info</function> returns information about + a <acronym>GiST</acronym> index opaque area, like the NSN, rightlink and + page type. + For example: +<screen> +test=# SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2)); + lsn | nsn | rightlink | flags +-----+-----+-----------+-------- + 0/1 | 0/0 | 1 | {leaf} +(1 row) +</screen> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <function>gist_page_items(page bytea) returns setof record</function> + <indexterm> + <primary>gist_page_items</primary> + </indexterm> + </term> + + <listitem> + <para> + <function>gist_page_items</function> returns information about + the data stored in a page of <acronym>GiST</acronym> index. For example: +<screen> +test=# SELECT * FROM gist_page_items('test_gist_idx', 0); + itemoffset | ctid | itemlen | keys +------------+-----------+---------+------------------- + 1 | (1,65535) | 40 | (p)=((166,166)) + 2 | (2,65535) | 40 | (p)=((332,332)) + 3 | (3,65535) | 40 | (p)=((498,498)) + 4 | (4,65535) | 40 | (p)=((664,664)) + 5 | (5,65535) | 40 | (p)=((830,830)) + 6 | (6,65535) | 40 | (p)=((996,996)) + 7 | (7,65535) | 40 | (p)=((1000,1000)) +(7 rows) +</screen> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <function>gist_page_items_bytea(page bytea) returns setof record</function> + <indexterm> + <primary>gist_page_items_bytea</primary> + </indexterm> + </term> + + <listitem> + <para> + <function>gist_page_items_bytea</function> returns information about + the data stored in a bytea copy of <acronym>GiST</acronym> page. For example: +<screen> +test=# SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 0)); + itemoffset | ctid | itemlen | tuple_data +------------+-----------+---------+------------------------------------------------------------------------------------ + 1 | (1,65535) | 40 | \x00000100ffff28000000000000c064400000000000c06440000000000000f03f000000000000f03f + 2 | (2,65535) | 40 | \x00000200ffff28000000000000c074400000000000c074400000000000e064400000000000e06440 + 3 | (3,65535) | 40 | \x00000300ffff28000000000000207f400000000000207f400000000000d074400000000000d07440 + 4 | (4,65535) | 40 | \x00000400ffff28000000000000c084400000000000c084400000000000307f400000000000307f40 + 5 | (5,65535) | 40 | \x00000500ffff28000000000000f089400000000000f089400000000000c884400000000000c88440 + 6 | (6,65535) | 40 | \x00000600ffff28000000000000208f400000000000208f400000000000f889400000000000f88940 + 7 | (7,65535) | 40 | \x00000700ffff28000000000000408f400000000000408f400000000000288f400000000000288f40 +(7 rows) +</screen> + </para> + </listitem> + </varlistentry> + </variablelist> + </sect2> + <sect2> <title>Hash Functions</title> -- 2.24.3 (Apple Git-128) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-12 13:49  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 3 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2021-01-12 13:49 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; Peter Geoghegan <pg@bowt.ie>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 10/12/2020 12:16, Andrey Borodin wrote:
>> 9 дек. 2020 г., в 14:47, Andrey Borodin <x4mmm@yandex-team.ru> написал(а):
>>> 7 дек. 2020 г., в 23:56, Peter Geoghegan <pg@bowt.ie> написал(а):
>>>
>>> On Mon, Dec 7, 2020 at 2:05 AM Andrey Borodin <x4mmm@yandex-team.ru> wrote:
>>>> Here's version with tests and docs. I still have no idea how to print some useful information about tuples keys.
>>>
>>> I suggest calling BuildIndexValueDescription() from your own custom
>>> debug instrumentation code.
>> Thanks for the hint, Peter!
>> This function does exactly what I want to do. But I have no Relation inside gist_page_items(bytea) function... probably, I'll add gist_page_items(relname, blockno) overload to fetch keys.
> 
> PFA patch with implementation.

I did a bit of cleanup on the function signature. The .sql script 
claimed that gist_page_items() took bytea as argument, but in reality it 
was a relation name, as text. I changed it so that it takes a page image 
as argument, instead of reading the block straight from the index. 
Mainly to make it consistent with brin_page_items(), if it wasn't for 
that precedence I might've gone either way on it.

Fixed the docs accordingly, and ran pgindent. New patch version attached.

- Heikki

Attachments:

v4-0001-Add-functions-to-pageinspect-to-inspect-GiST-inde.patch (text/x-patch, 23.5K) download From 791e45bcc86b205b372ea0679c8c036fff941f56 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas <heikki.linnakangas@iki.fi> Date: Tue, 12 Jan 2021 15:43:09 +0200 Subject: [PATCH v4 1/1] Add functions to 'pageinspect' to inspect GiST indexes. Author: Andrey Borodin and me Discussion: https://www.postgresql.org/message-id/3E4F9093-A1B5-4DF8-A292-0B48692E3954%40yandex-team.ru --- contrib/pageinspect/Makefile | 6 +- contrib/pageinspect/expected/gist.out | 152 ++++++++++ contrib/pageinspect/gistfuncs.c | 287 ++++++++++++++++++ contrib/pageinspect/pageinspect--1.8--1.9.sql | 41 +++ contrib/pageinspect/pageinspect.control | 2 +- contrib/pageinspect/sql/gist.sql | 23 ++ doc/src/sgml/pageinspect.sgml | 89 ++++++ 7 files changed, 597 insertions(+), 3 deletions(-) create mode 100644 contrib/pageinspect/expected/gist.out create mode 100644 contrib/pageinspect/gistfuncs.c create mode 100644 contrib/pageinspect/pageinspect--1.8--1.9.sql create mode 100644 contrib/pageinspect/sql/gist.sql diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index d9d8177116..4539f0aef7 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -7,19 +7,21 @@ OBJS = \ btreefuncs.o \ fsmfuncs.o \ ginfuncs.o \ + gistfuncs.o \ hashfuncs.o \ heapfuncs.o \ rawpage.o EXTENSION = pageinspect -DATA = pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \ +DATA = pageinspect--1.8--1.9.sql \ + pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \ pageinspect--1.5.sql pageinspect--1.5--1.6.sql \ pageinspect--1.4--1.5.sql pageinspect--1.3--1.4.sql \ pageinspect--1.2--1.3.sql pageinspect--1.1--1.2.sql \ pageinspect--1.0--1.1.sql PGFILEDESC = "pageinspect - functions to inspect contents of database pages" -REGRESS = page btree brin gin hash checksum +REGRESS = page btree brin gin gist hash checksum ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/pageinspect/expected/gist.out b/contrib/pageinspect/expected/gist.out new file mode 100644 index 0000000000..3c7e08b710 --- /dev/null +++ b/contrib/pageinspect/expected/gist.out @@ -0,0 +1,152 @@ +CREATE TABLE test_gist AS SELECT point(i,i) p, i::text t FROM + generate_series(1,1000) i; +CREATE INDEX test_gist_idx ON test_gist USING gist (p); +-- Page 0 is the root, the rest are leaf pages +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 0)); + lsn | nsn | rightlink | flags +-----+-----+------------+------- + 0/1 | 0/0 | 4294967295 | {} +(1 row) + +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 1)); + lsn | nsn | rightlink | flags +-----+-----+------------+-------- + 0/1 | 0/0 | 4294967295 | {leaf} +(1 row) + +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2)); + lsn | nsn | rightlink | flags +-----+-----+-----------+-------- + 0/1 | 0/0 | 1 | {leaf} +(1 row) + +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 0), 'test_gist_idx'); + itemoffset | ctid | itemlen | keys +------------+-----------+---------+------------------- + 1 | (1,65535) | 40 | (p)=((166,166)) + 2 | (2,65535) | 40 | (p)=((332,332)) + 3 | (3,65535) | 40 | (p)=((498,498)) + 4 | (4,65535) | 40 | (p)=((664,664)) + 5 | (5,65535) | 40 | (p)=((830,830)) + 6 | (6,65535) | 40 | (p)=((996,996)) + 7 | (7,65535) | 40 | (p)=((1000,1000)) +(7 rows) + +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 1), 'test_gist_idx') LIMIT 5; + itemoffset | ctid | itemlen | keys +------------+-------+---------+------------- + 1 | (0,1) | 40 | (p)=((1,1)) + 2 | (0,2) | 40 | (p)=((2,2)) + 3 | (0,3) | 40 | (p)=((3,3)) + 4 | (0,4) | 40 | (p)=((4,4)) + 5 | (0,5) | 40 | (p)=((5,5)) +(5 rows) + +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 2), 'test_gist_idx') LIMIT 5; + itemoffset | ctid | itemlen | keys +------------+--------+---------+----------------- + 1 | (1,10) | 40 | (p)=((167,167)) + 2 | (1,11) | 40 | (p)=((168,168)) + 3 | (1,12) | 40 | (p)=((169,169)) + 4 | (1,13) | 40 | (p)=((170,170)) + 5 | (1,14) | 40 | (p)=((171,171)) +(5 rows) + +\x +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 0)); +-[ RECORD 1 ]---------------------------------------------------------------------------------- +itemoffset | 1 +ctid | (1,65535) +itemlen | 40 +tuple_data | \x00000100ffff28000000000000c064400000000000c06440000000000000f03f000000000000f03f +-[ RECORD 2 ]---------------------------------------------------------------------------------- +itemoffset | 2 +ctid | (2,65535) +itemlen | 40 +tuple_data | \x00000200ffff28000000000000c074400000000000c074400000000000e064400000000000e06440 +-[ RECORD 3 ]---------------------------------------------------------------------------------- +itemoffset | 3 +ctid | (3,65535) +itemlen | 40 +tuple_data | \x00000300ffff28000000000000207f400000000000207f400000000000d074400000000000d07440 +-[ RECORD 4 ]---------------------------------------------------------------------------------- +itemoffset | 4 +ctid | (4,65535) +itemlen | 40 +tuple_data | \x00000400ffff28000000000000c084400000000000c084400000000000307f400000000000307f40 +-[ RECORD 5 ]---------------------------------------------------------------------------------- +itemoffset | 5 +ctid | (5,65535) +itemlen | 40 +tuple_data | \x00000500ffff28000000000000f089400000000000f089400000000000c884400000000000c88440 +-[ RECORD 6 ]---------------------------------------------------------------------------------- +itemoffset | 6 +ctid | (6,65535) +itemlen | 40 +tuple_data | \x00000600ffff28000000000000208f400000000000208f400000000000f889400000000000f88940 +-[ RECORD 7 ]---------------------------------------------------------------------------------- +itemoffset | 7 +ctid | (7,65535) +itemlen | 40 +tuple_data | \x00000700ffff28000000000000408f400000000000408f400000000000288f400000000000288f40 + +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 1)) LIMIT 5; +-[ RECORD 1 ]---------------------------------------------------------------------------------- +itemoffset | 1 +ctid | (0,1) +itemlen | 40 +tuple_data | \x0000000001002800000000000000f03f000000000000f03f000000000000f03f000000000000f03f +-[ RECORD 2 ]---------------------------------------------------------------------------------- +itemoffset | 2 +ctid | (0,2) +itemlen | 40 +tuple_data | \x00000000020028000000000000000040000000000000004000000000000000400000000000000040 +-[ RECORD 3 ]---------------------------------------------------------------------------------- +itemoffset | 3 +ctid | (0,3) +itemlen | 40 +tuple_data | \x00000000030028000000000000000840000000000000084000000000000008400000000000000840 +-[ RECORD 4 ]---------------------------------------------------------------------------------- +itemoffset | 4 +ctid | (0,4) +itemlen | 40 +tuple_data | \x00000000040028000000000000001040000000000000104000000000000010400000000000001040 +-[ RECORD 5 ]---------------------------------------------------------------------------------- +itemoffset | 5 +ctid | (0,5) +itemlen | 40 +tuple_data | \x00000000050028000000000000001440000000000000144000000000000014400000000000001440 + +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 2)) LIMIT 5; +-[ RECORD 1 ]---------------------------------------------------------------------------------- +itemoffset | 1 +ctid | (1,10) +itemlen | 40 +tuple_data | \x000001000a0028000000000000e064400000000000e064400000000000e064400000000000e06440 +-[ RECORD 2 ]---------------------------------------------------------------------------------- +itemoffset | 2 +ctid | (1,11) +itemlen | 40 +tuple_data | \x000001000b0028000000000000006540000000000000654000000000000065400000000000006540 +-[ RECORD 3 ]---------------------------------------------------------------------------------- +itemoffset | 3 +ctid | (1,12) +itemlen | 40 +tuple_data | \x000001000c0028000000000000206540000000000020654000000000002065400000000000206540 +-[ RECORD 4 ]---------------------------------------------------------------------------------- +itemoffset | 4 +ctid | (1,13) +itemlen | 40 +tuple_data | \x000001000d0028000000000000406540000000000040654000000000004065400000000000406540 +-[ RECORD 5 ]---------------------------------------------------------------------------------- +itemoffset | 5 +ctid | (1,14) +itemlen | 40 +tuple_data | \x000001000e0028000000000000606540000000000060654000000000006065400000000000606540 + +-- error cases +SELECT * FROM gist_page_items_bytea(get_raw_page('nonexistent_idx', 0)); +ERROR: relation "nonexistent_idx" does not exist +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 20000)); +ERROR: block number 20000 is out of range for relation "test_gist_idx" +DROP TABLE test_gist; diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c new file mode 100644 index 0000000000..146b2e91b6 --- /dev/null +++ b/contrib/pageinspect/gistfuncs.c @@ -0,0 +1,287 @@ +/* + * gistfuncs.c + * Functions to investigate the content of GiST indexes + * + * Copyright (c) 2014-2020, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/pageinspect/gistfuncs.c + */ +#include "postgres.h" + +#include "access/gist.h" +#include "access/gist_private.h" +#include "access/htup.h" +#include "access/relation.h" +#include "catalog/namespace.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "pageinspect.h" +#include "storage/itemptr.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/rel.h" +#include "utils/pg_lsn.h" +#include "utils/varlena.h" + +PG_FUNCTION_INFO_V1(gist_page_opaque_info); +PG_FUNCTION_INFO_V1(gist_page_items); +PG_FUNCTION_INFO_V1(gist_page_items_bytea); + +#define ItemPointerGetDatum(X) PointerGetDatum(X) + + +Datum +gist_page_opaque_info(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + TupleDesc tupdesc; + Page page; + GISTPageOpaque opaq; + HeapTuple resultTuple; + Datum values[4]; + bool nulls[4]; + Datum flags[16]; + int nflags = 0; + uint16 flagbits; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + page = get_page_from_raw(raw_page); + + opaq = (GISTPageOpaque) PageGetSpecialPointer(page); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Convert the flags bitmask to an array of human-readable names */ + flagbits = opaq->flags; + if (flagbits & F_LEAF) + flags[nflags++] = CStringGetTextDatum("leaf"); + if (flagbits & F_DELETED) + flags[nflags++] = CStringGetTextDatum("deleted"); + if (flagbits & F_TUPLES_DELETED) + flags[nflags++] = CStringGetTextDatum("tuples_deleted"); + if (flagbits & F_FOLLOW_RIGHT) + flags[nflags++] = CStringGetTextDatum("follow_right"); + if (flagbits & F_HAS_GARBAGE) + flags[nflags++] = CStringGetTextDatum("has_garbage"); + flagbits &= ~(F_LEAF | F_DELETED | F_TUPLES_DELETED | F_FOLLOW_RIGHT | F_HAS_GARBAGE); + if (flagbits) + { + /* any flags we don't recognize are printed in hex */ + flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits)); + } + + memset(nulls, 0, sizeof(nulls)); + + values[0] = LSNGetDatum(PageGetLSN(page)); + values[1] = LSNGetDatum(GistPageGetNSN(page)); + values[2] = Int64GetDatum(opaq->rightlink); + values[3] = PointerGetDatum(construct_array(flags, nflags, + TEXTOID, + -1, false, TYPALIGN_INT)); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(tupdesc, values, nulls); + + return HeapTupleGetDatum(resultTuple); +} + +typedef struct gist_page_items_state +{ + Page page; + TupleDesc tupd; + OffsetNumber offset; + Relation rel; +} gist_page_items_state; + +Datum +gist_page_items_bytea(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + FuncCallContext *fctx; + gist_page_items_state *inter_call_data; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + if (SRF_IS_FIRSTCALL()) + { + TupleDesc tupdesc; + MemoryContext mctx; + Page page; + + fctx = SRF_FIRSTCALL_INIT(); + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + page = get_page_from_raw(raw_page); + + inter_call_data = palloc(sizeof(gist_page_items_state)); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + if (GistPageIsDeleted(page)) + elog(NOTICE, "page is deleted"); + + inter_call_data->page = page; + inter_call_data->tupd = tupdesc; + inter_call_data->offset = FirstOffsetNumber; + + fctx->max_calls = PageGetMaxOffsetNumber(page); + fctx->user_fctx = inter_call_data; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + inter_call_data = fctx->user_fctx; + + if (fctx->call_cntr < fctx->max_calls) + { + Page page = inter_call_data->page; + OffsetNumber offset = inter_call_data->offset; + HeapTuple resultTuple; + Datum result; + Datum values[4]; + bool nulls[4]; + ItemId id; + IndexTuple itup; + bytea *tuple_bytea; + int tuple_len; + + id = PageGetItemId(page, offset); + + if (!ItemIdIsValid(id)) + elog(ERROR, "invalid ItemId"); + + itup = (IndexTuple) PageGetItem(page, id); + tuple_len = IndexTupleSize(itup); + + memset(nulls, 0, sizeof(nulls)); + + values[0] = DatumGetInt16(offset); + values[1] = ItemPointerGetDatum(&itup->t_tid); + values[2] = Int32GetDatum((int) IndexTupleSize(itup)); + + tuple_bytea = (bytea *) palloc(tuple_len + VARHDRSZ); + SET_VARSIZE(tuple_bytea, tuple_len + VARHDRSZ); + memcpy(VARDATA(tuple_bytea), itup, tuple_len); + values[3] = PointerGetDatum(tuple_bytea); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + inter_call_data->offset++; + SRF_RETURN_NEXT(fctx, result); + } + + SRF_RETURN_DONE(fctx); +} + +Datum +gist_page_items(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + Oid indexRelid = PG_GETARG_OID(1); + FuncCallContext *fctx; + gist_page_items_state *inter_call_data; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + if (SRF_IS_FIRSTCALL()) + { + Relation indexRel; + TupleDesc tupdesc; + MemoryContext mctx; + Page page; + + fctx = SRF_FIRSTCALL_INIT(); + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + page = get_page_from_raw(raw_page); + + inter_call_data = palloc(sizeof(gist_page_items_state)); + + /* Open the relation */ + indexRel = index_open(indexRelid, AccessShareLock); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + if (GistPageIsDeleted(page)) + elog(NOTICE, "page is deleted"); + + inter_call_data->page = page; + inter_call_data->tupd = tupdesc; + inter_call_data->offset = FirstOffsetNumber; + inter_call_data->rel = indexRel; + + fctx->max_calls = PageGetMaxOffsetNumber(page); + fctx->user_fctx = inter_call_data; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + inter_call_data = fctx->user_fctx; + + if (fctx->call_cntr < fctx->max_calls) + { + Page page = inter_call_data->page; + OffsetNumber offset = inter_call_data->offset; + HeapTuple resultTuple; + Datum result; + Datum values[4]; + bool nulls[4]; + ItemId id; + IndexTuple itup; + Datum itup_values[INDEX_MAX_KEYS]; + bool itup_isnull[INDEX_MAX_KEYS]; + char *key_desc; + + id = PageGetItemId(page, offset); + + if (!ItemIdIsValid(id)) + elog(ERROR, "invalid ItemId"); + + itup = (IndexTuple) PageGetItem(page, id); + + index_deform_tuple(itup, RelationGetDescr(inter_call_data->rel), + itup_values, itup_isnull); + + key_desc = BuildIndexValueDescription(inter_call_data->rel, itup_values, + itup_isnull); + + memset(nulls, 0, sizeof(nulls)); + + values[0] = DatumGetInt16(offset); + values[1] = ItemPointerGetDatum(&itup->t_tid); + values[2] = Int32GetDatum((int) IndexTupleSize(itup)); + values[3] = CStringGetTextDatum(key_desc); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + inter_call_data->offset++; + SRF_RETURN_NEXT(fctx, result); + } + + relation_close(inter_call_data->rel, AccessShareLock); + + SRF_RETURN_DONE(fctx); +} diff --git a/contrib/pageinspect/pageinspect--1.8--1.9.sql b/contrib/pageinspect/pageinspect--1.8--1.9.sql new file mode 100644 index 0000000000..9dc342fabc --- /dev/null +++ b/contrib/pageinspect/pageinspect--1.8--1.9.sql @@ -0,0 +1,41 @@ +/* contrib/pageinspect/pageinspect--1.8--1.9.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.9'" to load this file. \quit + +-- +-- gist_page_opaque_info() +-- +CREATE FUNCTION gist_page_opaque_info(IN page bytea, + OUT lsn pg_lsn, + OUT nsn pg_lsn, + OUT rightlink bigint, + OUT flags text[]) +AS 'MODULE_PATHNAME', 'gist_page_opaque_info' +LANGUAGE C STRICT PARALLEL SAFE; + + +-- +-- gist_page_items_bytea() +-- +CREATE FUNCTION gist_page_items_bytea(IN page bytea, + OUT itemoffset smallint, + OUT ctid tid, + OUT itemlen smallint, + OUT key_data bytea) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'gist_page_items_bytea' +LANGUAGE C STRICT PARALLEL SAFE; + +-- +-- gist_page_items() +-- +CREATE FUNCTION gist_page_items(IN page bytea, + IN index_oid regclass, + OUT itemoffset smallint, + OUT ctid tid, + OUT itemlen smallint, + OUT keys text) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'gist_page_items' +LANGUAGE C STRICT PARALLEL SAFE; diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control index f8cdf526c6..bd716769a1 100644 --- a/contrib/pageinspect/pageinspect.control +++ b/contrib/pageinspect/pageinspect.control @@ -1,5 +1,5 @@ # pageinspect extension comment = 'inspect the contents of database pages at a low level' -default_version = '1.8' +default_version = '1.9' module_pathname = '$libdir/pageinspect' relocatable = true diff --git a/contrib/pageinspect/sql/gist.sql b/contrib/pageinspect/sql/gist.sql new file mode 100644 index 0000000000..a6b17ef919 --- /dev/null +++ b/contrib/pageinspect/sql/gist.sql @@ -0,0 +1,23 @@ +CREATE TABLE test_gist AS SELECT point(i,i) p, i::text t FROM + generate_series(1,1000) i; +CREATE INDEX test_gist_idx ON test_gist USING gist (p); + +-- Page 0 is the root, the rest are leaf pages +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 0)); +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 1)); +SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2)); + +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 0), 'test_gist_idx'); +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 1), 'test_gist_idx') LIMIT 5; +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 2), 'test_gist_idx') LIMIT 5; + +\x +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 0)); +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 1)) LIMIT 5; +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 2)) LIMIT 5; + +-- error cases +SELECT * FROM gist_page_items_bytea(get_raw_page('nonexistent_idx', 0)); +SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 20000)); + +DROP TABLE test_gist; diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml index 687c3606ba..35858e1557 100644 --- a/doc/src/sgml/pageinspect.sgml +++ b/doc/src/sgml/pageinspect.sgml @@ -671,6 +671,95 @@ test=# SELECT first_tid, nbytes, tids[0:5] AS some_tids </variablelist> </sect2> + <sect2> + <title>GiST Functions</title> + + <variablelist> + <varlistentry> + <term> + <function>gist_page_opaque_info(page bytea) returns record</function> + <indexterm> + <primary>gist_page_opaque_info</primary> + </indexterm> + </term> + + <listitem> + <para> + <function>gist_page_opaque_info</function> returns information about + a <acronym>GiST</acronym> index opaque area, like the NSN, rightlink and + page type. + For example: +<screen> +test=# SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2)); + lsn | nsn | rightlink | flags +-----+-----+-----------+-------- + 0/1 | 0/0 | 1 | {leaf} +(1 row) +</screen> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <function>gist_page_items(page bytea, index oid) returns setof record</function> + <indexterm> + <primary>gist_page_items</primary> + </indexterm> + </term> + + <listitem> + <para> + <function>gist_page_items</function> returns information about + the data stored in a page of <acronym>GiST</acronym> index. For example: +<screen> +test=# SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 0), 'test_gist_idx'); + itemoffset | ctid | itemlen | keys +------------+-----------+---------+------------------- + 1 | (1,65535) | 40 | (p)=((166,166)) + 2 | (2,65535) | 40 | (p)=((332,332)) + 3 | (3,65535) | 40 | (p)=((498,498)) + 4 | (4,65535) | 40 | (p)=((664,664)) + 5 | (5,65535) | 40 | (p)=((830,830)) + 6 | (6,65535) | 40 | (p)=((996,996)) + 7 | (7,65535) | 40 | (p)=((1000,1000)) +(7 rows) +</screen> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <function>gist_page_items_bytea(page bytea) returns setof record</function> + <indexterm> + <primary>gist_page_items_bytea</primary> + </indexterm> + </term> + + <listitem> + <para> + Same as <function>gist_page_items</function>, but returns the key data as a raw + bytea blob. For example: +<screen> +test=# SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 0)); + itemoffset | ctid | itemlen | key_data +------------+-----------+---------+------------------------------------------------------------------------------------ + 1 | (1,65535) | 40 | \x00000100ffff28000000000000c064400000000000c06440000000000000f03f000000000000f03f + 2 | (2,65535) | 40 | \x00000200ffff28000000000000c074400000000000c074400000000000e064400000000000e06440 + 3 | (3,65535) | 40 | \x00000300ffff28000000000000207f400000000000207f400000000000d074400000000000d07440 + 4 | (4,65535) | 40 | \x00000400ffff28000000000000c084400000000000c084400000000000307f400000000000307f40 + 5 | (5,65535) | 40 | \x00000500ffff28000000000000f089400000000000f089400000000000c884400000000000c88440 + 6 | (6,65535) | 40 | \x00000600ffff28000000000000208f400000000000208f400000000000f889400000000000f88940 + 7 | (7,65535) | 40 | \x00000700ffff28000000000000408f400000000000408f400000000000288f400000000000288f40 +(7 rows) +</screen> + </para> + </listitem> + </varlistentry> + </variablelist> + </sect2> + <sect2> <title>Hash Functions</title> -- 2.29.2
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-12 16:19  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  2 siblings, 2 replies; 111+ messages in thread

From: Andrey Borodin @ 2021-01-12 16:19 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 12 янв. 2021 г., в 18:49, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
>> PFA patch with implementation.
> 
> I did a bit of cleanup on the function signature. The .sql script claimed that gist_page_items() took bytea as argument, but in reality it was a relation name, as text. I changed it so that it takes a page image as argument, instead of reading the block straight from the index. Mainly to make it consistent with brin_page_items(), if it wasn't for that precedence I might've gone either way on it.
bt_page_items() takes relation name and block number, that was a reason for doing so. But all others *_page_items() (heap, brin, hash) are doing as in v4. So I think it's more common way.

> 
> Fixed the docs accordingly, and ran pgindent. New patch version attached.

Thanks! Looks good to me.

One more question: will bytea tests run correctly on 32bit\different-endian systems?

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-13 08:41  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  1 sibling, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2021-01-13 08:41 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 12/01/2021 18:19, Andrey Borodin wrote:
>> 12 янв. 2021 г., в 18:49, Heikki Linnakangas <hlinnaka@iki.fi>
>> написал(а):
>> 
>> Fixed the docs accordingly, and ran pgindent. New patch version
>> attached.
> 
> Thanks! Looks good to me.

Pushed, thanks!

> One more question: will bytea tests run correctly on
> 32bit\different-endian systems?

Good question. Somehow I thought we were printing esseantilly text 
values as bytea. But they are Points, which consists of float8's. Since 
I already pushed this, I'm going to just wait and see what the buildfarm 
says, and fix if needed. I think the fix is going to be to just remove 
the test for the bytea-variant, it doesn't seem that important to test.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-13 09:46  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 2 replies; 111+ messages in thread

From: Andrey Borodin @ 2021-01-13 09:46 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 13 янв. 2021 г., в 13:41, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
>> One more question: will bytea tests run correctly on
>> 32bit\different-endian systems?
> 
> Good question. Somehow I thought we were printing esseantilly text values as bytea. But they are Points, which consists of float8's. Since I already pushed this, I'm going to just wait and see what the buildfarm says, and fix if needed. I think the fix is going to be to just remove the test for the bytea-variant, it doesn't seem that important to test.

Maybe we can just omit key_data from tests?

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-13 10:34  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  1 sibling, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2021-01-13 10:34 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 13/01/2021 11:46, Andrey Borodin wrote:
>> 13 янв. 2021 г., в 13:41, Heikki Linnakangas <hlinnaka@iki.fi>
>> написал(а):
>> 
>>> One more question: will bytea tests run correctly on 
>>> 32bit\different-endian systems?
>> 
>> Good question. Somehow I thought we were printing esseantilly text
>> values as bytea. But they are Points, which consists of float8's.
>> Since I already pushed this, I'm going to just wait and see what
>> the buildfarm says, and fix if needed. I think the fix is going to
>> be to just remove the test for the bytea-variant, it doesn't seem
>> that important to test.
> 
> Maybe we can just omit key_data from tests?

Make sense, fixed it that way. Thanks!

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-13 11:53  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2021-01-13 11:53 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 13/01/2021 12:34, Heikki Linnakangas wrote:
> On 13/01/2021 11:46, Andrey Borodin wrote:
>>> 13 янв. 2021 г., в 13:41, Heikki Linnakangas <hlinnaka@iki.fi>
>>> написал(а):
>>>
>>>> One more question: will bytea tests run correctly on
>>>> 32bit\different-endian systems?
>>>
>>> Good question. Somehow I thought we were printing esseantilly text
>>> values as bytea. But they are Points, which consists of float8's.
>>> Since I already pushed this, I'm going to just wait and see what
>>> the buildfarm says, and fix if needed. I think the fix is going to
>>> be to just remove the test for the bytea-variant, it doesn't seem
>>> that important to test.
>>
>> Maybe we can just omit key_data from tests?
> 
> Make sense, fixed it that way. Thanks!

Buildfarm animal thorntail is still not happy:

> --- /home/nm/farm/sparc64_deb10_gcc_64_ubsan/HEAD/pgsql.build/../pgsql/contrib/pageinspect/expected/gist.out	2021-01-13 13:38:09.721752365 +0300
> +++ /home/nm/farm/sparc64_deb10_gcc_64_ubsan/HEAD/pgsql.build/contrib/pageinspect/results/gist.out	2021-01-13 14:12:21.540046507 +0300
> @@ -3,21 +3,21 @@
>  CREATE INDEX test_gist_idx ON test_gist USING gist (p);
>  -- Page 0 is the root, the rest are leaf pages
>  SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 0));
> - lsn | nsn | rightlink  | flags 
> ------+-----+------------+-------
> - 0/1 | 0/0 | 4294967295 | {}
> +    lsn     | nsn | rightlink  | flags 
> +------------+-----+------------+-------
> + 0/1B8357F8 | 0/0 | 4294967295 | {}
>  (1 row)

Looks like the LSN on the page is not set to GistBuildLSN as expected. 
Weird.

Thorntail is a sparc64 system, so little-endian, but the other 
little-endian buildfarm members are not reporting this error. Any idea 
what might be going on?

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-13 16:17  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  1 sibling, 0 replies; 111+ messages in thread

From: Tom Lane @ 2021-01-13 16:17 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>; Peter Geoghegan <pg@bowt.ie>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

Heikki Linnakangas <hlinnaka@iki.fi> writes:
> On 13/01/2021 11:46, Andrey Borodin wrote:
>> Maybe we can just omit key_data from tests?

> Make sense, fixed it that way. Thanks!

thorntail, at least, is still unhappy.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-13 18:04  Heikki Linnakangas <hlinnaka@gmail.com>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2021-01-13 18:04 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



On 13 January 2021 13:53:39 EET, Heikki Linnakangas <hlinnaka@iki.fi> wrote:
>Buildfarm animal thorntail is still not happy:
>
>> --- /home/nm/farm/sparc64_deb10_gcc_64_ubsan/HEAD/pgsql.build/../pgsql/contrib/pageinspect/expected/gist.out	2021-01-13 13:38:09.721752365 +0300
>> +++ /home/nm/farm/sparc64_deb10_gcc_64_ubsan/HEAD/pgsql.build/contrib/pageinspect/results/gist.out	2021-01-13 14:12:21.540046507 +0300
>> @@ -3,21 +3,21 @@
>>  CREATE INDEX test_gist_idx ON test_gist USING gist (p);
>>  -- Page 0 is the root, the rest are leaf pages
>>  SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 0));
>> - lsn | nsn | rightlink  | flags 
>> ------+-----+------------+-------
>> - 0/1 | 0/0 | 4294967295 | {}
>> +    lsn     | nsn | rightlink  | flags 
>> +------------+-----+------------+-------
>> + 0/1B8357F8 | 0/0 | 4294967295 | {}
>>  (1 row)
>
>Looks like the LSN on the page is not set to GistBuildLSN as expected. 
>Weird.
>
>Thorntail is a sparc64 system, so little-endian, but the other 
>little-endian buildfarm members are not reporting this error. Any idea 
>what might be going on?

Status update on this: I am building Postgres in a qemu sparc64 emulated virtual machine, hoping to be able to reproduce this. It's very slow, so it will take hours still to complete.

I don't think this is a problem with the test, or with the new pageinspect functions, but a genuine bug in the gist building  code. Or there is something special on that animal that causes the just-created index pages to be dirtied. It does seem to happen consistently on thorntail, but not on other animals.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-13 18:16  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Heikki Linnakangas <hlinnaka@gmail.com>
  0 siblings, 0 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2021-01-13 18:16 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@gmail.com>; Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 13 January 2021 20:04:10 EET, Heikki Linnakangas <hlinnaka@gmail.com> wrote:
>
>
>On 13 January 2021 13:53:39 EET, Heikki Linnakangas <hlinnaka@iki.fi> wrote:
>>Looks like the LSN on the page is not set to GistBuildLSN as expected. 
>>Weird.
>>
>>Thorntail is a sparc64 system, so little-endian, but the other 
>>little-endian buildfarm members are not reporting this error. Any idea 
>>what might be going on?
>
>Status update on this: I am building Postgres in a qemu sparc64 emulated virtual machine, hoping to be able to reproduce this. It's very slow, so it will take hours still to complete.
>
>I don't think this is a problem with the test, or with the new pageinspect functions, but a genuine bug in the gist building  code. Or there is something special on that animal that causes the just-created index pages to be dirtied. It does seem to happen consistently on thorntail, but not on other animals.

Ah, silly me. Thorntail uses "wal_level=minimal". With that, I can readily reproduce this.

I'm still not sure why it happens, but now it should be straightforward to debug.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-13 23:47  Peter Geoghegan <pg@bowt.ie>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  2 siblings, 1 reply; 111+ messages in thread

From: Peter Geoghegan @ 2021-01-13 23:47 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Tue, Jan 12, 2021 at 5:49 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
> I did a bit of cleanup on the function signature. The .sql script
> claimed that gist_page_items() took bytea as argument, but in reality it
> was a relation name, as text. I changed it so that it takes a page image
> as argument, instead of reading the block straight from the index.
> Mainly to make it consistent with brin_page_items(), if it wasn't for
> that precedence I might've gone either way on it.

BTW it would be nice if gist_page_items() had a "dead" boolean output
argument for the item's LP_DEAD bit, just like bt_page_items(). I plan
on adding some testing for GiST's opportunistic index deletion soon. I
may also add some of the same enhancements that nbtree got today
(following commit d168b666).

This feature was originally heavily based on the nbtree LP_DEAD
deletion mechanism (now called simple deletion), and I see no reason
(or at least no good reason) why it shouldn't be possible to keep it
in sync (except maybe with bottom-up deletion, where that it at least
isn't straightforward/mechanical).

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-14 09:52  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Peter Geoghegan <pg@bowt.ie>
  0 siblings, 0 replies; 111+ messages in thread

From: Andrey Borodin @ 2021-01-14 09:52 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 14 янв. 2021 г., в 04:47, Peter Geoghegan <pg@bowt.ie> написал(а):
> 
> On Tue, Jan 12, 2021 at 5:49 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
>> I did a bit of cleanup on the function signature. The .sql script
>> claimed that gist_page_items() took bytea as argument, but in reality it
>> was a relation name, as text. I changed it so that it takes a page image
>> as argument, instead of reading the block straight from the index.
>> Mainly to make it consistent with brin_page_items(), if it wasn't for
>> that precedence I might've gone either way on it.
> 
> BTW it would be nice if gist_page_items() had a "dead" boolean output
> argument for the item's LP_DEAD bit, just like bt_page_items().
+1. PFA patch.

> I plan
> on adding some testing for GiST's opportunistic index deletion soon. I
> may also add some of the same enhancements that nbtree got today
> (following commit d168b666).
> 
> This feature was originally heavily based on the nbtree LP_DEAD
> deletion mechanism (now called simple deletion), and I see no reason
> (or at least no good reason) why it shouldn't be possible to keep it
> in sync (except maybe with bottom-up deletion, where that it at least
> isn't straightforward/mechanical).

Sound great!

Best regards, Andrey Borodin.

Attachments:

0001-Add-bool-column-for-LP_DEAF-flag-to-GiST-pageinspect.patch (application/octet-stream, 5.2K) download From f907a58a7139b871fc353368fe8c52e3fcfbcf6f Mon Sep 17 00:00:00 2001 From: Andrey Borodin <amborodin@acm.org> Date: Thu, 14 Jan 2021 14:49:29 +0500 Subject: [PATCH] Add bool column for LP_DEAF flag to GiST pageinspect functions --- contrib/pageinspect/expected/gist.out | 32 +++++++++---------- contrib/pageinspect/gistfuncs.c | 14 ++++---- contrib/pageinspect/pageinspect--1.8--1.9.sql | 2 ++ 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/contrib/pageinspect/expected/gist.out b/contrib/pageinspect/expected/gist.out index 5f7d8cea71..86c9e9caa9 100644 --- a/contrib/pageinspect/expected/gist.out +++ b/contrib/pageinspect/expected/gist.out @@ -31,25 +31,25 @@ SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2)); COMMIT; SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 0), 'test_gist_idx'); - itemoffset | ctid | itemlen | keys -------------+-----------+---------+------------------- - 1 | (1,65535) | 40 | (p)=((166,166)) - 2 | (2,65535) | 40 | (p)=((332,332)) - 3 | (3,65535) | 40 | (p)=((498,498)) - 4 | (4,65535) | 40 | (p)=((664,664)) - 5 | (5,65535) | 40 | (p)=((830,830)) - 6 | (6,65535) | 40 | (p)=((996,996)) - 7 | (7,65535) | 40 | (p)=((1000,1000)) + itemoffset | ctid | itemlen | dead | keys +------------+-----------+---------+------+------------------- + 1 | (1,65535) | 40 | f | (p)=((166,166)) + 2 | (2,65535) | 40 | f | (p)=((332,332)) + 3 | (3,65535) | 40 | f | (p)=((498,498)) + 4 | (4,65535) | 40 | f | (p)=((664,664)) + 5 | (5,65535) | 40 | f | (p)=((830,830)) + 6 | (6,65535) | 40 | f | (p)=((996,996)) + 7 | (7,65535) | 40 | f | (p)=((1000,1000)) (7 rows) SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 1), 'test_gist_idx') LIMIT 5; - itemoffset | ctid | itemlen | keys -------------+-------+---------+------------- - 1 | (0,1) | 40 | (p)=((1,1)) - 2 | (0,2) | 40 | (p)=((2,2)) - 3 | (0,3) | 40 | (p)=((3,3)) - 4 | (0,4) | 40 | (p)=((4,4)) - 5 | (0,5) | 40 | (p)=((5,5)) + itemoffset | ctid | itemlen | dead | keys +------------+-------+---------+------+------------- + 1 | (0,1) | 40 | f | (p)=((1,1)) + 2 | (0,2) | 40 | f | (p)=((2,2)) + 3 | (0,3) | 40 | f | (p)=((3,3)) + 4 | (0,4) | 40 | f | (p)=((4,4)) + 5 | (0,5) | 40 | f | (p)=((5,5)) (5 rows) -- gist_page_items_bytea prints the raw key data as a bytea. The output of that is diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c index 146b2e91b6..fc35b49024 100644 --- a/contrib/pageinspect/gistfuncs.c +++ b/contrib/pageinspect/gistfuncs.c @@ -151,8 +151,8 @@ gist_page_items_bytea(PG_FUNCTION_ARGS) OffsetNumber offset = inter_call_data->offset; HeapTuple resultTuple; Datum result; - Datum values[4]; - bool nulls[4]; + Datum values[5]; + bool nulls[5]; ItemId id; IndexTuple itup; bytea *tuple_bytea; @@ -175,7 +175,8 @@ gist_page_items_bytea(PG_FUNCTION_ARGS) tuple_bytea = (bytea *) palloc(tuple_len + VARHDRSZ); SET_VARSIZE(tuple_bytea, tuple_len + VARHDRSZ); memcpy(VARDATA(tuple_bytea), itup, tuple_len); - values[3] = PointerGetDatum(tuple_bytea); + values[3] = BoolGetDatum(ItemIdIsDead(id)); + values[4] = PointerGetDatum(tuple_bytea); /* Build and return the result tuple. */ resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); @@ -245,8 +246,8 @@ gist_page_items(PG_FUNCTION_ARGS) OffsetNumber offset = inter_call_data->offset; HeapTuple resultTuple; Datum result; - Datum values[4]; - bool nulls[4]; + Datum values[5]; + bool nulls[5]; ItemId id; IndexTuple itup; Datum itup_values[INDEX_MAX_KEYS]; @@ -271,7 +272,8 @@ gist_page_items(PG_FUNCTION_ARGS) values[0] = DatumGetInt16(offset); values[1] = ItemPointerGetDatum(&itup->t_tid); values[2] = Int32GetDatum((int) IndexTupleSize(itup)); - values[3] = CStringGetTextDatum(key_desc); + values[3] = BoolGetDatum(ItemIdIsDead(id)); + values[4] = CStringGetTextDatum(key_desc); /* Build and return the result tuple. */ resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); diff --git a/contrib/pageinspect/pageinspect--1.8--1.9.sql b/contrib/pageinspect/pageinspect--1.8--1.9.sql index 9dc342fabc..e2a704b6e8 100644 --- a/contrib/pageinspect/pageinspect--1.8--1.9.sql +++ b/contrib/pageinspect/pageinspect--1.8--1.9.sql @@ -22,6 +22,7 @@ CREATE FUNCTION gist_page_items_bytea(IN page bytea, OUT itemoffset smallint, OUT ctid tid, OUT itemlen smallint, + OUT dead boolean, OUT key_data bytea) RETURNS SETOF record AS 'MODULE_PATHNAME', 'gist_page_items_bytea' @@ -35,6 +36,7 @@ CREATE FUNCTION gist_page_items(IN page bytea, OUT itemoffset smallint, OUT ctid tid, OUT itemlen smallint, + OUT dead boolean, OUT keys text) RETURNS SETOF record AS 'MODULE_PATHNAME', 'gist_page_items' -- 2.24.3 (Apple Git-128) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-15 05:24  Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  2 siblings, 1 reply; 111+ messages in thread

From: Peter Eisentraut @ 2021-01-15 05:24 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; Andrey Borodin <x4mmm@yandex-team.ru>; Peter Geoghegan <pg@bowt.ie>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 2021-01-12 14:49, Heikki Linnakangas wrote:
>>>> I suggest calling BuildIndexValueDescription() from your own custom
>>>> debug instrumentation code.
>>> Thanks for the hint, Peter!
>>> This function does exactly what I want to do. But I have no Relation inside gist_page_items(bytea) function... probably, I'll add gist_page_items(relname, blockno) overload to fetch keys.
>>
>> PFA patch with implementation.
> 
> I did a bit of cleanup on the function signature. The .sql script
> claimed that gist_page_items() took bytea as argument, but in reality it
> was a relation name, as text. I changed it so that it takes a page image
> as argument, instead of reading the block straight from the index.
> Mainly to make it consistent with brin_page_items(), if it wasn't for
> that precedence I might've gone either way on it.

I noticed this patch while working on another patch for pageinspect [0], 
and this one appears to introduce a problem similar to the one the other 
patch attempts to fix: The "itemlen" output parameters are declared to 
be of type smallint, but the underlying C data is of type uint16 
(OffsetNumber).  I don't know the details of gist enough to determine 
whether overflow is possible here.  If not, perhaps a check or at least 
a comment would be useful.  Otherwise, these parameters should be of 
type int in SQL.

[0]: 
https://www.postgresql.org/message-id/09e2dd82-4eb6-bba1-271a-d2b58bf6c71f@enterprisedb.com





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-15 17:05  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  0 siblings, 0 replies; 111+ messages in thread

From: Andrey Borodin @ 2021-01-15 17:05 UTC (permalink / raw)
  To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers



> 15 янв. 2021 г., в 10:24, Peter Eisentraut <peter.eisentraut@enterprisedb.com> написал(а):
> 
> I noticed this patch while working on another patch for pageinspect [0], and this one appears to introduce a problem similar to the one the other patch attempts to fix: The "itemlen" output parameters are declared to be of type smallint, but the underlying C data is of type uint16 (OffsetNumber).  I don't know the details of gist enough to determine whether overflow is possible here.  If not, perhaps a check or at least a comment would be useful.  Otherwise, these parameters should be of type int in SQL.

Item offsets cannot exceed maximum block size of 32768. And even 32768/sizeof(ItemId). Thus overflow is impossible.
Interesting question is wether pageinspect should protect itself from corrupted input?
Generating description from bogus tuple, probably, can go wrong.

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-17 20:50  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  1 sibling, 1 reply; 111+ messages in thread

From: Tom Lane @ 2021-01-17 20:50 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>; Peter Geoghegan <pg@bowt.ie>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

Heikki Linnakangas <hlinnaka@iki.fi> writes:
> On 12/01/2021 18:19, Andrey Borodin wrote:
>> Thanks! Looks good to me.

> Pushed, thanks!

I noticed that gist_page_items() thinks it can hold inter_call_data->rel
open across a series of calls.  That's completely unsafe: the executor
might not run the call series to completion (see LIMIT), resulting in
relcache leak complaints.  I suspect that it might have cache-flush
hazards even without that.  I think this code needs to be rewritten to do
all the interesting work in the first call.  Or maybe better, return the
results as a tuplestore so you don't have to do multiple calls at all.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-17 22:35  Peter Geoghegan <pg@bowt.ie>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 1 reply; 111+ messages in thread

From: Peter Geoghegan @ 2021-01-17 22:35 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Andrey Borodin <x4mmm@yandex-team.ru>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Sun, Jan 17, 2021 at 12:50 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> I noticed that gist_page_items() thinks it can hold inter_call_data->rel
> open across a series of calls.  That's completely unsafe: the executor
> might not run the call series to completion (see LIMIT), resulting in
> relcache leak complaints.

It also has the potential to run into big problems should the user
input a raw page image with an regclass-argument-incompatible tuple
descriptor. Maybe that's okay (this is a tool for experts), but it
certainly is a consideration.

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-17 22:52  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Peter Geoghegan <pg@bowt.ie>
  0 siblings, 2 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2021-01-17 22:52 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 18/01/2021 00:35, Peter Geoghegan wrote:
> On Sun, Jan 17, 2021 at 12:50 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> I noticed that gist_page_items() thinks it can hold inter_call_data->rel
>> open across a series of calls.  That's completely unsafe: the executor
>> might not run the call series to completion (see LIMIT), resulting in
>> relcache leak complaints.

Fixed, thanks! I changed it to return a tuplestore.

> It also has the potential to run into big problems should the user
> input a raw page image with an regclass-argument-incompatible tuple
> descriptor. Maybe that's okay (this is a tool for experts), but it
> certainly is a consideration.

I'm not sure I understand. It's true that the raw page image can contain 
data from a different index, or any garbage really. And the function 
will behave badly if you do that. That's an accepted risk with 
pageinspect functions, that's why they're superuser-only, although some 
of them are more tolerant of corrupt pages than others. The 
gist_page_items_bytea() variant doesn't try to parse the key data and is 
less likely to crash on bad input.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-17 23:04  Peter Geoghegan <pg@bowt.ie>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  1 sibling, 1 reply; 111+ messages in thread

From: Peter Geoghegan @ 2021-01-17 23:04 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Andrey Borodin <x4mmm@yandex-team.ru>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Sun, Jan 17, 2021 at 2:52 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
> I'm not sure I understand. It's true that the raw page image can contain
> data from a different index, or any garbage really. And the function
> will behave badly if you do that. That's an accepted risk with
> pageinspect functions, that's why they're superuser-only, although some
> of them are more tolerant of corrupt pages than others. The
> gist_page_items_bytea() variant doesn't try to parse the key data and is
> less likely to crash on bad input.

I personally agree with you - it's not like there aren't other ways
for superusers to crash the server (most of which seem very similar to
this gist_page_items() issue, in fact). I just think that it's worth
being clear about that being a trade-off that we've accepted.

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-17 23:10  Peter Geoghegan <pg@bowt.ie>
  parent: Peter Geoghegan <pg@bowt.ie>
  0 siblings, 1 reply; 111+ messages in thread

From: Peter Geoghegan @ 2021-01-17 23:10 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Andrey Borodin <x4mmm@yandex-team.ru>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Sun, Jan 17, 2021 at 3:04 PM Peter Geoghegan <pg@bowt.ie> wrote:
> I personally agree with you - it's not like there aren't other ways
> for superusers to crash the server (most of which seem very similar to
> this gist_page_items() issue, in fact). I just think that it's worth
> being clear about that being a trade-off that we've accepted.

Can we rename gist_page_items_bytea() to gist_page_items(), and at the
same time rename the current gist_page_items() -- perhaps call it
gist_page_items_output()?

That way we could add a bt_page_items_output() function later, while
leaving everything consistent (actually not quite, since
bt_page_items() outputs text instead of bytea -- but that seems worth
fixing too). This also has the merit of making the unsafe "output"
variant into the special case.

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-01-18 12:40  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Peter Geoghegan <pg@bowt.ie>
  0 siblings, 0 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2021-01-18 12:40 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Andrey Borodin <x4mmm@yandex-team.ru>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On 18/01/2021 01:10, Peter Geoghegan wrote:
> On Sun, Jan 17, 2021 at 3:04 PM Peter Geoghegan <pg@bowt.ie> wrote:
>> I personally agree with you - it's not like there aren't other ways
>> for superusers to crash the server (most of which seem very similar to
>> this gist_page_items() issue, in fact). I just think that it's worth
>> being clear about that being a trade-off that we've accepted.
> 
> Can we rename gist_page_items_bytea() to gist_page_items(), and at the
> same time rename the current gist_page_items() -- perhaps call it
> gist_page_items_output()?
> 
> That way we could add a bt_page_items_output() function later, while
> leaving everything consistent (actually not quite, since
> bt_page_items() outputs text instead of bytea -- but that seems worth
> fixing too). This also has the merit of making the unsafe "output"
> variant into the special case.

bt_page_items() and bt_page_items_bytea() exist already. And 
brin_page_items() also calls the output functions (there's no bytea 
version of that). Perhaps it would've been better to make the 
bytea-variants the default, but I'm afraid that ship has already sailed.

We're not terribly consistent; heap_page_items(), hash_page_items() and 
gin_page_items() don't attempt to call the output functions.

Then again, I don't think we need to worry much about backwards 
compatibility in pageinspect, so I guess we could rename them all. It 
doesn't bother me enough to make me do it, but I won't object if you 
want to.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-03-08 14:41  Ibrar Ahmed <ibrar.ahmad@gmail.com>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  1 sibling, 1 reply; 111+ messages in thread

From: Ibrar Ahmed @ 2021-03-08 14:41 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Mon, Jan 18, 2021 at 3:52 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

> On 18/01/2021 00:35, Peter Geoghegan wrote:
> > On Sun, Jan 17, 2021 at 12:50 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> >> I noticed that gist_page_items() thinks it can hold inter_call_data->rel
> >> open across a series of calls.  That's completely unsafe: the executor
> >> might not run the call series to completion (see LIMIT), resulting in
> >> relcache leak complaints.
>
> Fixed, thanks! I changed it to return a tuplestore.
>
> > It also has the potential to run into big problems should the user
> > input a raw page image with an regclass-argument-incompatible tuple
> > descriptor. Maybe that's okay (this is a tool for experts), but it
> > certainly is a consideration.
>
> I'm not sure I understand. It's true that the raw page image can contain
> data from a different index, or any garbage really. And the function
> will behave badly if you do that. That's an accepted risk with
> pageinspect functions, that's why they're superuser-only, although some
> of them are more tolerant of corrupt pages than others. The
> gist_page_items_bytea() variant doesn't try to parse the key data and is
> less likely to crash on bad input.
>
> - Heikki
>
>
> The patch (0001-Add-bool-column-for-LP_DEAF-flag-to-GiST-pageinspect.patch
)
does not apply successfully and has multiple hanks failed.

http://cfbot.cputube.org/patch_32_2824.log

patching file contrib/pageinspect/gistfuncs.c
Hunk #1 FAILED at 151.
Hunk #2 FAILED at 175.
Hunk #3 FAILED at 245.
Hunk #4 FAILED at 271.

...

Can we get a rebase?

I am marking the patch "Waiting on Author"

-- 
Ibrar Ahmed

^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-03-08 15:59  Peter Geoghegan <pg@bowt.ie>
  parent: Ibrar Ahmed <ibrar.ahmad@gmail.com>
  0 siblings, 1 reply; 111+ messages in thread

From: Peter Geoghegan @ 2021-03-08 15:59 UTC (permalink / raw)
  To: Ibrar Ahmed <ibrar.ahmad@gmail.com>; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>; Heikki Linnakangas <hlinnaka@iki.fi>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Mon, Mar 8, 2021 at 6:41 AM Ibrar Ahmed <ibrar.ahmad@gmail.com> wrote:
> The patch (0001-Add-bool-column-for-LP_DEAF-flag-to-GiST-pageinspect.patch)
> does not apply successfully and has multiple hanks failed.

That's because it was committed.

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-03-08 16:15  Ibrar Ahmed <ibrar.ahmad@gmail.com>
  parent: Peter Geoghegan <pg@bowt.ie>
  0 siblings, 1 reply; 111+ messages in thread

From: Ibrar Ahmed @ 2021-03-08 16:15 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; +Cc: Andrey Borodin <x4mmm@yandex-team.ru>; Heikki Linnakangas <hlinnaka@iki.fi>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

On Mon, Mar 8, 2021 at 8:59 PM Peter Geoghegan <pg@bowt.ie> wrote:

> On Mon, Mar 8, 2021 at 6:41 AM Ibrar Ahmed <ibrar.ahmad@gmail.com> wrote:
> > The patch
> (0001-Add-bool-column-for-LP_DEAF-flag-to-GiST-pageinspect.patch)
> > does not apply successfully and has multiple hanks failed.
>
> That's because it was committed.
>
> Thanks for the clarification, its status was not changed which confused me
:)



> --
> Peter Geoghegan
>


-- 
Ibrar Ahmed

^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-03-08 17:06  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Ibrar Ahmed <ibrar.ahmad@gmail.com>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2021-03-08 17:06 UTC (permalink / raw)
  To: Ibrar Ahmed <ibrar.ahmad@gmail.com>; +Cc: Peter Geoghegan <pg@bowt.ie>; Heikki Linnakangas <hlinnaka@iki.fi>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers

Thanks, Ibrar!

> 8 марта 2021 г., в 21:15, Ibrar Ahmed <ibrar.ahmad@gmail.com> написал(а):
> 
> 
> 
> On Mon, Mar 8, 2021 at 8:59 PM Peter Geoghegan <pg@bowt.ie> wrote:
> On Mon, Mar 8, 2021 at 6:41 AM Ibrar Ahmed <ibrar.ahmad@gmail.com> wrote:
> > The patch (0001-Add-bool-column-for-LP_DEAF-flag-to-GiST-pageinspect.patch)
> > does not apply successfully and has multiple hanks failed.
> 
> That's because it was committed.
> 
> Thanks for the clarification, its status was not changed which confused me :)
> 

There were numerous GiST-build-related patches in this thread. Yet uncommitted is a patch with sortsupport routines for btree_gist contrib module.
Here's its version which needs review.

Thanks for bringing this up!

Best regards, Andrey Borodin.

Attachments:

v5-0001-Sortsupport-for-sorting-GiST-build-for-gist_btree.patch (application/octet-stream, 81.5K) download From 19e22aaa451143e42d42d821a61457d37a16078f Mon Sep 17 00:00:00 2001 From: Andrey Borodin <amborodin@acm.org> Date: Sat, 3 Oct 2020 21:08:54 +0500 Subject: [PATCH v5] Sortsupport for sorting GiST build for gist_btree types --- contrib/btree_gist/Makefile | 2 +- contrib/btree_gist/btree_bit.c | 24 +++ contrib/btree_gist/btree_bytea.c | 26 ++- contrib/btree_gist/btree_cash.c | 75 ++++++++ contrib/btree_gist/btree_date.c | 27 +++ contrib/btree_gist/btree_enum.c | 70 ++++++++ contrib/btree_gist/btree_float4.c | 71 ++++++++ contrib/btree_gist/btree_float8.c | 80 +++++++++ contrib/btree_gist/btree_gist--1.6--1.7.sql | 182 ++++++++++++++++++++ contrib/btree_gist/btree_gist.control | 2 +- contrib/btree_gist/btree_gist.h | 1 + contrib/btree_gist/btree_inet.c | 80 +++++++++ contrib/btree_gist/btree_int2.c | 70 ++++++++ contrib/btree_gist/btree_int4.c | 70 ++++++++ contrib/btree_gist/btree_int8.c | 74 ++++++++ contrib/btree_gist/btree_interval.c | 25 +++ contrib/btree_gist/btree_macaddr.c | 71 ++++++++ contrib/btree_gist/btree_macaddr8.c | 71 ++++++++ contrib/btree_gist/btree_numeric.c | 24 +++ contrib/btree_gist/btree_oid.c | 70 ++++++++ contrib/btree_gist/btree_text.c | 25 +++ contrib/btree_gist/btree_time.c | 25 +++ contrib/btree_gist/btree_ts.c | 25 +++ contrib/btree_gist/btree_uuid.c | 25 +++ contrib/btree_gist/expected/bit.out | 7 + contrib/btree_gist/expected/bytea.out | 7 + contrib/btree_gist/expected/cash.out | 7 + contrib/btree_gist/expected/char.out | 7 + contrib/btree_gist/expected/cidr.out | 7 + contrib/btree_gist/expected/date.out | 7 + contrib/btree_gist/expected/enum.out | 7 + contrib/btree_gist/expected/float4.out | 7 + contrib/btree_gist/expected/float8.out | 7 + contrib/btree_gist/expected/inet.out | 7 + contrib/btree_gist/expected/int2.out | 7 + contrib/btree_gist/expected/int4.out | 7 + contrib/btree_gist/expected/int8.out | 7 + contrib/btree_gist/expected/interval.out | 7 + contrib/btree_gist/expected/macaddr.out | 7 + contrib/btree_gist/expected/macaddr8.out | 7 + contrib/btree_gist/expected/numeric.out | 7 + contrib/btree_gist/expected/oid.out | 7 + contrib/btree_gist/expected/text.out | 7 + contrib/btree_gist/expected/time.out | 7 + contrib/btree_gist/expected/timestamp.out | 7 + contrib/btree_gist/expected/timestamptz.out | 7 + contrib/btree_gist/expected/timetz.out | 7 + contrib/btree_gist/expected/uuid.out | 7 + contrib/btree_gist/expected/varbit.out | 7 + contrib/btree_gist/expected/varchar.out | 7 + contrib/btree_gist/sql/bit.sql | 4 + contrib/btree_gist/sql/bytea.sql | 4 + contrib/btree_gist/sql/cash.sql | 4 + contrib/btree_gist/sql/char.sql | 4 + contrib/btree_gist/sql/cidr.sql | 4 + contrib/btree_gist/sql/date.sql | 4 + contrib/btree_gist/sql/enum.sql | 4 + contrib/btree_gist/sql/float4.sql | 4 + contrib/btree_gist/sql/float8.sql | 4 + contrib/btree_gist/sql/inet.sql | 4 + contrib/btree_gist/sql/int2.sql | 4 + contrib/btree_gist/sql/int4.sql | 4 + contrib/btree_gist/sql/int8.sql | 4 + contrib/btree_gist/sql/interval.sql | 4 + contrib/btree_gist/sql/macaddr.sql | 4 + contrib/btree_gist/sql/macaddr8.sql | 4 + contrib/btree_gist/sql/numeric.sql | 4 + contrib/btree_gist/sql/oid.sql | 4 + contrib/btree_gist/sql/text.sql | 4 + contrib/btree_gist/sql/time.sql | 4 + contrib/btree_gist/sql/timestamp.sql | 4 + contrib/btree_gist/sql/timestamptz.sql | 4 + contrib/btree_gist/sql/timetz.sql | 4 + contrib/btree_gist/sql/uuid.sql | 4 + contrib/btree_gist/sql/varbit.sql | 4 + contrib/btree_gist/sql/varchar.sql | 4 + src/backend/access/gist/gistbuild.c | 1 + 77 files changed, 1499 insertions(+), 3 deletions(-) create mode 100644 contrib/btree_gist/btree_gist--1.6--1.7.sql diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile index e92d974a1a..a1f818f71e 100644 --- a/contrib/btree_gist/Makefile +++ b/contrib/btree_gist/Makefile @@ -32,7 +32,7 @@ EXTENSION = btree_gist DATA = btree_gist--1.0--1.1.sql \ btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \ - btree_gist--1.5--1.6.sql + btree_gist--1.5--1.6.sql btree_gist--1.6--1.7.sql PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes" REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \ diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c index 2225244ded..e5d3ac6a24 100644 --- a/contrib/btree_gist/btree_bit.c +++ b/contrib/btree_gist/btree_bit.c @@ -19,6 +19,7 @@ PG_FUNCTION_INFO_V1(gbt_bit_picksplit); PG_FUNCTION_INFO_V1(gbt_bit_consistent); PG_FUNCTION_INFO_V1(gbt_bit_penalty); PG_FUNCTION_INFO_V1(gbt_bit_same); +PG_FUNCTION_INFO_V1(gbt_bit_sortsupport); /* define for comparison */ @@ -209,3 +210,26 @@ gbt_bit_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bit_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bit_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_bit_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_bytea.c b/contrib/btree_gist/btree_bytea.c index 6b005f0157..a2abfb7d7c 100644 --- a/contrib/btree_gist/btree_bytea.c +++ b/contrib/btree_gist/btree_bytea.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_bytea_picksplit); PG_FUNCTION_INFO_V1(gbt_bytea_consistent); PG_FUNCTION_INFO_V1(gbt_bytea_penalty); PG_FUNCTION_INFO_V1(gbt_bytea_same); +PG_FUNCTION_INFO_V1(gbt_bytea_sortsupport); /* define for comparison */ @@ -87,7 +88,7 @@ static const gbtree_vinfo tinfo = /************************************************** - * Text ops + * Bytea ops **************************************************/ @@ -168,3 +169,26 @@ gbt_bytea_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bytea_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bytea_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_bytea_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c index dfa23224b6..bba5c51a8e 100644 --- a/contrib/btree_gist/btree_cash.c +++ b/contrib/btree_gist/btree_cash.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_cash_consistent); PG_FUNCTION_INFO_V1(gbt_cash_distance); PG_FUNCTION_INFO_V1(gbt_cash_penalty); PG_FUNCTION_INFO_V1(gbt_cash_same); +PG_FUNCTION_INFO_V1(gbt_cash_sortsupport); static bool gbt_cashgt(const void *a, const void *b, FmgrInfo *flinfo) @@ -216,3 +217,77 @@ gbt_cash_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_cash_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + cashKEY *ia = (cashKEY *) DatumGetPointer(a); + cashKEY *ib = (cashKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_cash_abbrev_convert(Datum original, SortSupport ssup) +{ + cashKEY *b1 = (cashKEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gbt_cash_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_cash_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_cash_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_cash_cmp_abbrev; + ssup->abbrev_converter = gbt_cash_abbrev_convert; + ssup->abbrev_abort = gbt_cash_abbrev_abort; + ssup->abbrev_full_comparator = gbt_cash_sort_build_cmp; + } + else + { + ssup->comparator = gbt_cash_sort_build_cmp; + } + PG_RETURN_VOID(); +} + diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c index 455a265a49..e32a6739c9 100644 --- a/contrib/btree_gist/btree_date.c +++ b/contrib/btree_gist/btree_date.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_date_consistent); PG_FUNCTION_INFO_V1(gbt_date_distance); PG_FUNCTION_INFO_V1(gbt_date_penalty); PG_FUNCTION_INFO_V1(gbt_date_same); +PG_FUNCTION_INFO_V1(gbt_date_sortsupport); static bool gbt_dategt(const void *a, const void *b, FmgrInfo *flinfo) @@ -257,3 +258,29 @@ gbt_date_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_date_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + dateKEY *ia = (dateKEY *) PointerGetDatum(a); + dateKEY *ib = (dateKEY *) PointerGetDatum(b); + + return DatumGetInt32(DirectFunctionCall2(date_cmp, + DateADTGetDatum(ia->lower), + DateADTGetDatum(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_date_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_date_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_enum.c b/contrib/btree_gist/btree_enum.c index d4dc38a38e..41b487747e 100644 --- a/contrib/btree_gist/btree_enum.c +++ b/contrib/btree_gist/btree_enum.c @@ -26,6 +26,7 @@ PG_FUNCTION_INFO_V1(gbt_enum_picksplit); PG_FUNCTION_INFO_V1(gbt_enum_consistent); PG_FUNCTION_INFO_V1(gbt_enum_penalty); PG_FUNCTION_INFO_V1(gbt_enum_same); +PG_FUNCTION_INFO_V1(gbt_enum_sortsupport); static bool @@ -183,3 +184,72 @@ gbt_enum_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_enum_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_enum_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + Oid z = b1->lower; + + return (Datum) z; +} + +static int +gbt_enum_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_enum_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_enum_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_enum_cmp_abbrev; + ssup->abbrev_converter = gbt_enum_abbrev_convert; + ssup->abbrev_abort = gbt_enum_abbrev_abort; + ssup->abbrev_full_comparator = gbt_enum_sort_build_cmp; + } + else + { + ssup->comparator = gbt_enum_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c index 3604c73313..751e526b0a 100644 --- a/contrib/btree_gist/btree_float4.c +++ b/contrib/btree_gist/btree_float4.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float4_consistent); PG_FUNCTION_INFO_V1(gbt_float4_distance); PG_FUNCTION_INFO_V1(gbt_float4_penalty); PG_FUNCTION_INFO_V1(gbt_float4_same); +PG_FUNCTION_INFO_V1(gbt_float4_sortsupport); static bool gbt_float4gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -209,3 +210,73 @@ gbt_float4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + + +static int +gbt_float4_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + float4KEY *ia = (float4KEY *) DatumGetPointer(a); + float4KEY *ib = (float4KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float4_abbrev_convert(Datum original, SortSupport ssup) +{ + float4KEY *b1 = (float4KEY *) DatumGetPointer(original); + float4 z = b1->lower; + + return (Datum) Float4GetDatum(z); +} + +static int +gbt_float4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (DatumGetFloat4(z1) > DatumGetFloat4(z2)) + return 1; + else if (DatumGetFloat4(z1) < DatumGetFloat4(z2)) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_float4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float4_cmp_abbrev; + ssup->abbrev_converter = gbt_float4_abbrev_convert; + ssup->abbrev_abort = gbt_float4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float4_sort_build_cmp; + } + else + { + ssup->comparator = gbt_float4_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c index 10a5262aaa..b4e6a75361 100644 --- a/contrib/btree_gist/btree_float8.c +++ b/contrib/btree_gist/btree_float8.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float8_consistent); PG_FUNCTION_INFO_V1(gbt_float8_distance); PG_FUNCTION_INFO_V1(gbt_float8_penalty); PG_FUNCTION_INFO_V1(gbt_float8_same); +PG_FUNCTION_INFO_V1(gbt_float8_sortsupport); static bool @@ -216,3 +217,82 @@ gbt_float8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_float8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + float8KEY *ia = (float8KEY *) DatumGetPointer(a); + float8KEY *ib = (float8KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float8_abbrev_convert(Datum original, SortSupport ssup) +{ + float8KEY *b1 = (float8KEY *) DatumGetPointer(original); + float8 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) Float8GetDatum(z); +#else + return (Datum) Float4GetDatum((float)z); +#endif +} + +static int +gbt_float8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + if (DatumGetFloat8(z1) > DatumGetFloat8(z2)) + return 1; + else if (DatumGetFloat8(z1) < DatumGetFloat8(z2)) + return -1; + else + return 0; +#else + if (DatumGetFloat4(z1) > DatumGetFloat4(z2)) + return 1; + else if (DatumGetFloat4(z1) < DatumGetFloat4(z2)) + return -1; + else + return 0; +#endif +} + +static bool +gbt_float8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float8_cmp_abbrev; + ssup->abbrev_converter = gbt_float8_abbrev_convert; + ssup->abbrev_abort = gbt_float8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_float8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_gist--1.6--1.7.sql b/contrib/btree_gist/btree_gist--1.6--1.7.sql new file mode 100644 index 0000000000..abb5b8b0f4 --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.6--1.7.sql @@ -0,0 +1,182 @@ +/* contrib/btree_gist/btree_gist--1.6--1.7.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.7'" to load this file. \quit + + +CREATE FUNCTION gbt_int8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD + FUNCTION 11 (int8, int8) gbt_int8_sortsupport (internal) ; + +CREATE FUNCTION gbt_int4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD + FUNCTION 11 (int4, int4) gbt_int4_sortsupport (internal) ; + +CREATE FUNCTION gbt_int2_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD + FUNCTION 11 (int2, int2) gbt_int2_sortsupport (internal) ; + +CREATE FUNCTION gbt_float8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD + FUNCTION 11 (float8, float8) gbt_float8_sortsupport (internal) ; + +CREATE FUNCTION gbt_float4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD + FUNCTION 11 (float4, float4) gbt_float4_sortsupport (internal) ; + +CREATE FUNCTION gbt_enum_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD + FUNCTION 11 (anyenum, anyenum) gbt_enum_sortsupport (internal) ; + +CREATE FUNCTION gbt_oid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD + FUNCTION 11 (oid, oid) gbt_oid_sortsupport (internal) ; + +CREATE FUNCTION gbt_cash_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD + FUNCTION 11 (money, money) gbt_cash_sortsupport (internal) ; + +CREATE FUNCTION gbt_inet_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD + FUNCTION 11 (inet, inet) gbt_inet_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD + FUNCTION 11 (cidr, cidr) gbt_inet_sortsupport (internal) ; + + +CREATE FUNCTION gbt_macad_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD + FUNCTION 11 (macaddr, macaddr) gbt_macad_sortsupport (internal) ; + +CREATE FUNCTION gbt_macad8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD + FUNCTION 11 (macaddr8, macaddr8) gbt_macad8_sortsupport (internal) ; + +CREATE FUNCTION gbt_numeric_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD + FUNCTION 11 (numeric, numeric) gbt_numeric_sortsupport (internal) ; + +CREATE FUNCTION gbt_uuid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD + FUNCTION 11 (uuid, uuid) gbt_uuid_sortsupport (internal) ; + +CREATE FUNCTION gbt_ts_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD + FUNCTION 11 (timestamp, timestamp) gbt_ts_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (timestamptz, timestamptz) gbt_ts_sortsupport (internal) ; + +CREATE FUNCTION gbt_text_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_text_ops USING gist ADD + FUNCTION 11 (text, text) gbt_text_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD + FUNCTION 11 (bpchar, bpchar) gbt_text_sortsupport (internal) ; + +CREATE FUNCTION gbt_time_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_time_ops USING gist ADD + FUNCTION 11 (time, time) gbt_time_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD + FUNCTION 11 (timetz, timetz) gbt_time_sortsupport (internal) ; + +CREATE FUNCTION gbt_bytea_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD + FUNCTION 11 (bytea, bytea) gbt_bytea_sortsupport (internal) ; + +CREATE FUNCTION gbt_date_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_date_ops USING gist ADD + FUNCTION 11 (date, date) gbt_date_sortsupport (internal) ; + +CREATE FUNCTION gbt_bit_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD + FUNCTION 11 (bit, bit) gbt_bit_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD + FUNCTION 11 (varbit, varbit) gbt_bit_sortsupport (internal) ; + +CREATE FUNCTION gbt_intv_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD + FUNCTION 11 (interval, interval) gbt_intv_sortsupport (internal) ; + diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control index e5c41fe8f3..fa9171a80a 100644 --- a/contrib/btree_gist/btree_gist.control +++ b/contrib/btree_gist/btree_gist.control @@ -1,6 +1,6 @@ # btree_gist extension comment = 'support for indexing common datatypes in GiST' -default_version = '1.6' +default_version = '1.7' module_pathname = '$libdir/btree_gist' relocatable = true trusted = true diff --git a/contrib/btree_gist/btree_gist.h b/contrib/btree_gist/btree_gist.h index 14c7c8ee19..35ad287ed3 100644 --- a/contrib/btree_gist/btree_gist.h +++ b/contrib/btree_gist/btree_gist.h @@ -6,6 +6,7 @@ #include "access/nbtree.h" #include "fmgr.h" +#include "utils/sortsupport.h" #define BtreeGistNotEqualStrategyNumber 6 diff --git a/contrib/btree_gist/btree_inet.c b/contrib/btree_gist/btree_inet.c index e4b3a946b2..f80db5d4c3 100644 --- a/contrib/btree_gist/btree_inet.c +++ b/contrib/btree_gist/btree_inet.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_inet_picksplit); PG_FUNCTION_INFO_V1(gbt_inet_consistent); PG_FUNCTION_INFO_V1(gbt_inet_penalty); PG_FUNCTION_INFO_V1(gbt_inet_same); +PG_FUNCTION_INFO_V1(gbt_inet_sortsupport); static bool @@ -186,3 +187,82 @@ gbt_inet_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_inet_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + inetKEY *ia = (inetKEY *) DatumGetPointer(a); + inetKEY *ib = (inetKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_inet_abbrev_convert(Datum original, SortSupport ssup) +{ + inetKEY *b1 = (inetKEY *) DatumGetPointer(original); + double z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) Float8GetDatum(z); +#else + return (Datum) Float4GetDatum((float) z); +#endif +} + +static int +gbt_inet_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + if (DatumGetFloat8(z1) > DatumGetFloat8(z2)) + return 1; + else if (DatumGetFloat8(z1) < DatumGetFloat8(z2)) + return -1; + else + return 0; +#else + if (DatumGetFloat4(z1) > DatumGetFloat4(z2)) + return 1; + else if (DatumGetFloat4(z1) < DatumGetFloat4(z2)) + return -1; + else + return 0; +#endif +} + +static bool +gbt_inet_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_inet_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_inet_cmp_abbrev; + ssup->abbrev_converter = gbt_inet_abbrev_convert; + ssup->abbrev_abort = gbt_inet_abbrev_abort; + ssup->abbrev_full_comparator = gbt_inet_sort_build_cmp; + } + else + { + ssup->comparator = gbt_inet_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c index a91b95ff39..968c6950ca 100644 --- a/contrib/btree_gist/btree_int2.c +++ b/contrib/btree_gist/btree_int2.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int2_consistent); PG_FUNCTION_INFO_V1(gbt_int2_distance); PG_FUNCTION_INFO_V1(gbt_int2_penalty); PG_FUNCTION_INFO_V1(gbt_int2_same); +PG_FUNCTION_INFO_V1(gbt_int2_sortsupport); static bool gbt_int2gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -214,3 +215,72 @@ gbt_int2_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int2_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int16KEY *ia = (int16KEY *) DatumGetPointer(a); + int16KEY *ib = (int16KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int2_abbrev_convert(Datum original, SortSupport ssup) +{ + int16KEY *b1 = (int16KEY *) DatumGetPointer(original); + int16 z = b1->lower; + + return (Datum) z; +} + +static int +gbt_int2_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int2_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int2_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int2_cmp_abbrev; + ssup->abbrev_converter = gbt_int2_abbrev_convert; + ssup->abbrev_abort = gbt_int2_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int2_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int2_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c index 7ea98c478c..9f6f77ce43 100644 --- a/contrib/btree_gist/btree_int4.c +++ b/contrib/btree_gist/btree_int4.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int4_consistent); PG_FUNCTION_INFO_V1(gbt_int4_distance); PG_FUNCTION_INFO_V1(gbt_int4_penalty); PG_FUNCTION_INFO_V1(gbt_int4_same); +PG_FUNCTION_INFO_V1(gbt_int4_sortsupport); static bool @@ -215,3 +216,72 @@ gbt_int4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int4_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int32KEY *ia = (int32KEY *) DatumGetPointer(a); + int32KEY *ib = (int32KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int4_abbrev_convert(Datum original, SortSupport ssup) +{ + int32KEY *b1 = (int32KEY *) DatumGetPointer(original); + int32 z = b1->lower; + + return (Datum) z; +} + +static int +gbt_int4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int4_cmp_abbrev; + ssup->abbrev_converter = gbt_int4_abbrev_convert; + ssup->abbrev_abort = gbt_int4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int4_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int4_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c index df2b0d174b..5fd8001c48 100644 --- a/contrib/btree_gist/btree_int8.c +++ b/contrib/btree_gist/btree_int8.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int8_consistent); PG_FUNCTION_INFO_V1(gbt_int8_distance); PG_FUNCTION_INFO_V1(gbt_int8_penalty); PG_FUNCTION_INFO_V1(gbt_int8_same); +PG_FUNCTION_INFO_V1(gbt_int8_sortsupport); static bool @@ -215,3 +216,76 @@ gbt_int8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int64KEY *ia = (int64KEY *) DatumGetPointer(a); + int64KEY *ib = (int64KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int8_abbrev_convert(Datum original, SortSupport ssup) +{ + int64KEY *b1 = (int64KEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) (z >> 32); +#endif +} + +static int +gbt_int8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int8_cmp_abbrev; + ssup->abbrev_converter = gbt_int8_abbrev_convert; + ssup->abbrev_abort = gbt_int8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c index a4b3b2b1e6..2657213ec7 100644 --- a/contrib/btree_gist/btree_interval.c +++ b/contrib/btree_gist/btree_interval.c @@ -27,6 +27,7 @@ PG_FUNCTION_INFO_V1(gbt_intv_consistent); PG_FUNCTION_INFO_V1(gbt_intv_distance); PG_FUNCTION_INFO_V1(gbt_intv_penalty); PG_FUNCTION_INFO_V1(gbt_intv_same); +PG_FUNCTION_INFO_V1(gbt_intv_sortsupport); static bool @@ -297,3 +298,27 @@ gbt_intv_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_intv_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + intvKEY *ia = (intvKEY *) DatumGetPointer(a); + intvKEY *ib = (intvKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(interval_cmp, IntervalPGetDatum(&ia->lower), IntervalPGetDatum(&ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_intv_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_intv_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_macaddr.c b/contrib/btree_gist/btree_macaddr.c index 7f0e9e9c91..f43edad74e 100644 --- a/contrib/btree_gist/btree_macaddr.c +++ b/contrib/btree_gist/btree_macaddr.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad_picksplit); PG_FUNCTION_INFO_V1(gbt_macad_consistent); PG_FUNCTION_INFO_V1(gbt_macad_penalty); PG_FUNCTION_INFO_V1(gbt_macad_same); +PG_FUNCTION_INFO_V1(gbt_macad_sortsupport); static bool @@ -195,3 +196,73 @@ gbt_macad_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + macKEY *ma = (macKEY *) DatumGetPointer(a); + macKEY *mb = (macKEY *) DatumGetPointer(b); + uint64 ia = mac_2_uint64(&ma->lower); + uint64 ib = mac_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + { + return 0; + } + + return (ia > ib) ? 1 : -1; +} + +static Datum +gbt_macad_abbrev_convert(Datum original, SortSupport ssup) +{ + macKEY *b1 = (macKEY *) DatumGetPointer(original); + uint64 z = mac_2_uint64(&b1->lower); + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) z>>32; +#endif +} + +static int +gbt_macad_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +static bool +gbt_macad_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad_cmp_abbrev; + ssup->abbrev_converter = gbt_macad_abbrev_convert; + ssup->abbrev_abort = gbt_macad_abbrev_abort; + ssup->abbrev_full_comparator = gbt_macad_sort_build_cmp; + } + else + { + ssup->comparator = gbt_macad_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_macaddr8.c b/contrib/btree_gist/btree_macaddr8.c index ab4bca5d50..458c0bf6f7 100644 --- a/contrib/btree_gist/btree_macaddr8.c +++ b/contrib/btree_gist/btree_macaddr8.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad8_picksplit); PG_FUNCTION_INFO_V1(gbt_macad8_consistent); PG_FUNCTION_INFO_V1(gbt_macad8_penalty); PG_FUNCTION_INFO_V1(gbt_macad8_same); +PG_FUNCTION_INFO_V1(gbt_macad8_sortsupport); static bool @@ -195,3 +196,73 @@ gbt_macad8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + mac8KEY *ma = (mac8KEY *) DatumGetPointer(a); + mac8KEY *mb = (mac8KEY *) DatumGetPointer(b); + uint64 ia = mac8_2_uint64(&ma->lower); + uint64 ib = mac8_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + { + return 0; + } + + return (ia > ib) ? 1 : -1; +} + +static Datum +gbt_macad8_abbrev_convert(Datum original, SortSupport ssup) +{ + mac8KEY *b1 = (mac8KEY *) DatumGetPointer(original); + uint64 z = mac8_2_uint64(&b1->lower); + +#if SIZEOF_DATUM == 8 + return (Datum) z; +#else + return (Datum) z>>32; +#endif +} + +static int +gbt_macad8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +static bool +gbt_macad8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad8_cmp_abbrev; + ssup->abbrev_converter = gbt_macad8_abbrev_convert; + ssup->abbrev_abort = gbt_macad8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_macad8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_macad8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_numeric.c b/contrib/btree_gist/btree_numeric.c index 35e466cdd9..ed5305397a 100644 --- a/contrib/btree_gist/btree_numeric.c +++ b/contrib/btree_gist/btree_numeric.c @@ -21,6 +21,7 @@ PG_FUNCTION_INFO_V1(gbt_numeric_picksplit); PG_FUNCTION_INFO_V1(gbt_numeric_consistent); PG_FUNCTION_INFO_V1(gbt_numeric_penalty); PG_FUNCTION_INFO_V1(gbt_numeric_same); +PG_FUNCTION_INFO_V1(gbt_numeric_sortsupport); /* define for comparison */ @@ -227,3 +228,26 @@ gbt_numeric_picksplit(PG_FUNCTION_ARGS) &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(v); } + +static int +gbt_numeric_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(numeric_cmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_numeric_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_numeric_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c index 3cc7d4245d..a0d7066d92 100644 --- a/contrib/btree_gist/btree_oid.c +++ b/contrib/btree_gist/btree_oid.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_oid_consistent); PG_FUNCTION_INFO_V1(gbt_oid_distance); PG_FUNCTION_INFO_V1(gbt_oid_penalty); PG_FUNCTION_INFO_V1(gbt_oid_same); +PG_FUNCTION_INFO_V1(gbt_oid_sortsupport); static bool @@ -215,3 +216,72 @@ gbt_oid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_oid_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + { + return 0; + } + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_oid_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + Oid z = b1->lower; + + return (Datum) z; +} + +static int +gbt_oid_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + if (z1 > z2) + return 1; + else if (z1 < z2) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_oid_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_oid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_oid_cmp_abbrev; + ssup->abbrev_converter = gbt_oid_abbrev_convert; + ssup->abbrev_abort = gbt_oid_abbrev_abort; + ssup->abbrev_full_comparator = gbt_oid_sort_build_cmp; + } + else + { + ssup->comparator = gbt_oid_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 8019d11281..84200bafbf 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_text_consistent); PG_FUNCTION_INFO_V1(gbt_bpchar_consistent); PG_FUNCTION_INFO_V1(gbt_text_penalty); PG_FUNCTION_INFO_V1(gbt_text_same); +PG_FUNCTION_INFO_V1(gbt_text_sortsupport); /* define for comparison */ @@ -239,3 +240,27 @@ gbt_text_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_text_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2Coll(bttextcmp, + ssup->ssup_collation, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_text_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_text_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c index fd8774a2f0..d33917e8b0 100644 --- a/contrib/btree_gist/btree_time.c +++ b/contrib/btree_gist/btree_time.c @@ -28,6 +28,7 @@ PG_FUNCTION_INFO_V1(gbt_time_distance); PG_FUNCTION_INFO_V1(gbt_timetz_consistent); PG_FUNCTION_INFO_V1(gbt_time_penalty); PG_FUNCTION_INFO_V1(gbt_time_same); +PG_FUNCTION_INFO_V1(gbt_time_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -332,3 +333,27 @@ gbt_time_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_time_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + timeKEY *ia = (timeKEY *) DatumGetPointer(a); + timeKEY *ib = (timeKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(time_cmp, TimeADTGetDatumFast(ia->lower), TimeADTGetDatumFast(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_time_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_time_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c index 2671ba961c..0fa9f53383 100644 --- a/contrib/btree_gist/btree_ts.c +++ b/contrib/btree_gist/btree_ts.c @@ -31,6 +31,7 @@ PG_FUNCTION_INFO_V1(gbt_tstz_consistent); PG_FUNCTION_INFO_V1(gbt_tstz_distance); PG_FUNCTION_INFO_V1(gbt_ts_penalty); PG_FUNCTION_INFO_V1(gbt_ts_same); +PG_FUNCTION_INFO_V1(gbt_ts_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -399,3 +400,27 @@ gbt_ts_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_ts_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + tsKEY *ia = (tsKEY *) DatumGetPointer(a); + tsKEY *ib = (tsKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(timestamp_cmp, TimestampGetDatumFast(ia->lower), TimestampGetDatumFast(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_ts_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_ts_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_uuid.c b/contrib/btree_gist/btree_uuid.c index b81875979a..18618f9495 100644 --- a/contrib/btree_gist/btree_uuid.c +++ b/contrib/btree_gist/btree_uuid.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_uuid_picksplit); PG_FUNCTION_INFO_V1(gbt_uuid_consistent); PG_FUNCTION_INFO_V1(gbt_uuid_penalty); PG_FUNCTION_INFO_V1(gbt_uuid_same); +PG_FUNCTION_INFO_V1(gbt_uuid_sortsupport); static int @@ -233,3 +234,27 @@ gbt_uuid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_uuid_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + uuidKEY *ua = (uuidKEY *) DatumGetPointer(a); + uuidKEY *ub = (uuidKEY *) DatumGetPointer(b); + + return uuid_internal_cmp(&ua->lower, &ub->lower); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_uuid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_uuid_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/expected/bit.out b/contrib/btree_gist/expected/bit.out index e57871f310..fca3a20eec 100644 --- a/contrib/btree_gist/expected/bit.out +++ b/contrib/btree_gist/expected/bit.out @@ -32,7 +32,14 @@ SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; 350 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); +DEBUG: building index "bitidx" on table "bittmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "bitidx_b" on table "bittmp" serially +DROP INDEX bitidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM bittmp WHERE a < '011011000100010111011000110000100'; count diff --git a/contrib/btree_gist/expected/bytea.out b/contrib/btree_gist/expected/bytea.out index b9efa73c08..d40974025e 100644 --- a/contrib/btree_gist/expected/bytea.out +++ b/contrib/btree_gist/expected/bytea.out @@ -33,7 +33,14 @@ SELECT count(*) FROM byteatmp WHERE a > '31b0'; 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); +DEBUG: building index "byteaidx" on table "byteatmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "byteaidx_b" on table "byteatmp" serially +DROP INDEX byteaidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM byteatmp WHERE a < '31b0'::bytea; count diff --git a/contrib/btree_gist/expected/cash.out b/contrib/btree_gist/expected/cash.out index 7fbc735592..650a5b8635 100644 --- a/contrib/btree_gist/expected/cash.out +++ b/contrib/btree_gist/expected/cash.out @@ -40,7 +40,14 @@ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; $21,915.01 | $442.22 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); +DEBUG: building index "moneyidx" on table "moneytmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "moneyidx_b" on table "moneytmp" serially +DROP INDEX moneyidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM moneytmp WHERE a < '22649.64'::money; count diff --git a/contrib/btree_gist/expected/char.out b/contrib/btree_gist/expected/char.out index d715c045cc..9ead7907cf 100644 --- a/contrib/btree_gist/expected/char.out +++ b/contrib/btree_gist/expected/char.out @@ -32,7 +32,14 @@ SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); +DEBUG: building index "charidx" on table "chartmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "charidx_b" on table "chartmp" serially +DROP INDEX charidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM chartmp WHERE a < '31b0'::char(32); count diff --git a/contrib/btree_gist/expected/cidr.out b/contrib/btree_gist/expected/cidr.out index 6d0995add6..fd6b3d9657 100644 --- a/contrib/btree_gist/expected/cidr.out +++ b/contrib/btree_gist/expected/cidr.out @@ -32,7 +32,14 @@ SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; 309 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); +DEBUG: building index "cidridx" on table "cidrtmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "cidridx_b" on table "cidrtmp" serially +DROP INDEX cidridx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM cidrtmp WHERE a < '121.111.63.82'::cidr; count diff --git a/contrib/btree_gist/expected/date.out b/contrib/btree_gist/expected/date.out index 5db864bb82..df7e1b3d3e 100644 --- a/contrib/btree_gist/expected/date.out +++ b/contrib/btree_gist/expected/date.out @@ -40,7 +40,14 @@ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; 03-24-2001 | 39 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); +DEBUG: building index "dateidx" on table "datetmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "dateidx_b" on table "datetmp" serially +DROP INDEX dateidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM datetmp WHERE a < '2001-02-13'::date; count diff --git a/contrib/btree_gist/expected/enum.out b/contrib/btree_gist/expected/enum.out index c4b769dd4b..15cd580c8e 100644 --- a/contrib/btree_gist/expected/enum.out +++ b/contrib/btree_gist/expected/enum.out @@ -46,7 +46,14 @@ SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; 230 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); +DEBUG: building index "enumidx" on table "enumtmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "enumidx_b" on table "enumtmp" serially +DROP INDEX enumidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow; count diff --git a/contrib/btree_gist/expected/float4.out b/contrib/btree_gist/expected/float4.out index dfe732049e..b3cfa72dba 100644 --- a/contrib/btree_gist/expected/float4.out +++ b/contrib/btree_gist/expected/float4.out @@ -40,7 +40,14 @@ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; -158.17741 | 20.822586 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); +DEBUG: building index "float4idx" on table "float4tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "float4idx_b" on table "float4tmp" serially +DROP INDEX float4idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM float4tmp WHERE a < -179.0::float4; count diff --git a/contrib/btree_gist/expected/float8.out b/contrib/btree_gist/expected/float8.out index ebd0ef3d68..e90a061f68 100644 --- a/contrib/btree_gist/expected/float8.out +++ b/contrib/btree_gist/expected/float8.out @@ -40,7 +40,14 @@ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; -1769.73634 | 120.26366000000007 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); +DEBUG: building index "float8idx" on table "float8tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "float8idx_b" on table "float8tmp" serially +DROP INDEX float8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM float8tmp WHERE a < -1890.0::float8; count diff --git a/contrib/btree_gist/expected/inet.out b/contrib/btree_gist/expected/inet.out index c323d903da..e11d83a955 100644 --- a/contrib/btree_gist/expected/inet.out +++ b/contrib/btree_gist/expected/inet.out @@ -32,7 +32,14 @@ SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; 386 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); +DEBUG: building index "inetidx" on table "inettmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "inetidx_b" on table "inettmp" serially +DROP INDEX inetidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM inettmp WHERE a < '89.225.196.191'::inet; count diff --git a/contrib/btree_gist/expected/int2.out b/contrib/btree_gist/expected/int2.out index 50a332939b..c695767bd2 100644 --- a/contrib/btree_gist/expected/int2.out +++ b/contrib/btree_gist/expected/int2.out @@ -40,7 +40,14 @@ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); +DEBUG: building index "int2idx" on table "int2tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int2idx_b" on table "int2tmp" serially +DROP INDEX int2idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int2tmp WHERE a < 237::int2; count diff --git a/contrib/btree_gist/expected/int4.out b/contrib/btree_gist/expected/int4.out index 6bbdc7c3f4..3a45937ca1 100644 --- a/contrib/btree_gist/expected/int4.out +++ b/contrib/btree_gist/expected/int4.out @@ -40,7 +40,14 @@ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); +DEBUG: building index "int4idx" on table "int4tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int4idx_b" on table "int4tmp" serially +DROP INDEX int4idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int4tmp WHERE a < 237::int4; count diff --git a/contrib/btree_gist/expected/int8.out b/contrib/btree_gist/expected/int8.out index eff77c26b5..ee7bbae0fc 100644 --- a/contrib/btree_gist/expected/int8.out +++ b/contrib/btree_gist/expected/int8.out @@ -40,7 +40,14 @@ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' 478227196042750 | 13655904687909 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); +DEBUG: building index "int8idx" on table "int8tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int8idx_b" on table "int8tmp" serially +DROP INDEX int8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int8tmp WHERE a < 464571291354841::int8; count diff --git a/contrib/btree_gist/expected/interval.out b/contrib/btree_gist/expected/interval.out index 4c3d494e4a..d720ffbccc 100644 --- a/contrib/btree_gist/expected/interval.out +++ b/contrib/btree_gist/expected/interval.out @@ -40,7 +40,14 @@ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21 @ 220 days 19 hours 5 mins 42 secs | @ 21 days -2 hours -15 mins -41 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); +DEBUG: building index "intervalidx" on table "intervaltmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "intervalidx_b" on table "intervaltmp" serially +DROP INDEX intervalidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM intervaltmp WHERE a < '199 days 21:21:23'::interval; count diff --git a/contrib/btree_gist/expected/macaddr.out b/contrib/btree_gist/expected/macaddr.out index c0a4c6287f..81fe01eaa4 100644 --- a/contrib/btree_gist/expected/macaddr.out +++ b/contrib/btree_gist/expected/macaddr.out @@ -32,7 +32,14 @@ SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); +DEBUG: building index "macaddridx" on table "macaddrtmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "macaddridx_b" on table "macaddrtmp" serially +DROP INDEX macaddridx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM macaddrtmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr; count diff --git a/contrib/btree_gist/expected/macaddr8.out b/contrib/btree_gist/expected/macaddr8.out index e5ec6a5dea..30afd94380 100644 --- a/contrib/btree_gist/expected/macaddr8.out +++ b/contrib/btree_gist/expected/macaddr8.out @@ -32,7 +32,14 @@ SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); +DEBUG: building index "macaddr8idx" on table "macaddr8tmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "macaddr8idx_b" on table "macaddr8tmp" serially +DROP INDEX macaddr8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM macaddr8tmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr8; count diff --git a/contrib/btree_gist/expected/numeric.out b/contrib/btree_gist/expected/numeric.out index ae839b8ec8..0b0d491a40 100644 --- a/contrib/btree_gist/expected/numeric.out +++ b/contrib/btree_gist/expected/numeric.out @@ -94,7 +94,14 @@ SELECT count(*) FROM numerictmp WHERE a > 0 ; 576 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); +DEBUG: building index "numericidx" on table "numerictmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "numericidx_b" on table "numerictmp" serially +DROP INDEX numericidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM numerictmp WHERE a < -1890.0; count diff --git a/contrib/btree_gist/expected/oid.out b/contrib/btree_gist/expected/oid.out index 776bbb1026..3c8746483f 100644 --- a/contrib/btree_gist/expected/oid.out +++ b/contrib/btree_gist/expected/oid.out @@ -32,7 +32,14 @@ SELECT count(*) FROM oidtmp WHERE oid > 17; 983 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); +DEBUG: building index "oididx" on table "oidtmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); +DEBUG: building index "oididx_b" on table "oidtmp" serially +DROP INDEX oididx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM oidtmp WHERE oid < 17; count diff --git a/contrib/btree_gist/expected/text.out b/contrib/btree_gist/expected/text.out index bb4e2e62d1..4f17ac413f 100644 --- a/contrib/btree_gist/expected/text.out +++ b/contrib/btree_gist/expected/text.out @@ -33,7 +33,14 @@ SELECT count(*) FROM texttmp WHERE a > '31b0'; 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); +DEBUG: building index "textidx" on table "texttmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "textidx_b" on table "texttmp" serially +DROP INDEX textidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM texttmp WHERE a < '31b0'::text; count diff --git a/contrib/btree_gist/expected/time.out b/contrib/btree_gist/expected/time.out index ec95ef77c5..a71dfc980a 100644 --- a/contrib/btree_gist/expected/time.out +++ b/contrib/btree_gist/expected/time.out @@ -40,7 +40,14 @@ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; 10:55:32 | @ 1 min 39 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); +DEBUG: building index "timeidx" on table "timetmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timeidx_b" on table "timetmp" serially +DROP INDEX timeidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timetmp WHERE a < '10:57:11'::time; count diff --git a/contrib/btree_gist/expected/timestamp.out b/contrib/btree_gist/expected/timestamp.out index 0d94f2f245..b96d5ade8c 100644 --- a/contrib/btree_gist/expected/timestamp.out +++ b/contrib/btree_gist/expected/timestamp.out @@ -40,7 +40,14 @@ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10- Mon Nov 29 20:12:43 2004 | @ 34 days 11 hours 17 mins 35 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); +DEBUG: building index "timestampidx" on table "timestamptmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timestampidx_b" on table "timestamptmp" serially +DROP INDEX timestampidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timestamptmp WHERE a < '2004-10-26 08:55:08'::timestamp; count diff --git a/contrib/btree_gist/expected/timestamptz.out b/contrib/btree_gist/expected/timestamptz.out index 75a15a4256..72f466fd7c 100644 --- a/contrib/btree_gist/expected/timestamptz.out +++ b/contrib/btree_gist/expected/timestamptz.out @@ -100,7 +100,14 @@ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> ' Thu Jan 24 12:28:12 2019 PST | @ 37 days 7 hours 28 mins 18 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); +DEBUG: building index "timestamptzidx" on table "timestamptztmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timestamptzidx_b" on table "timestamptztmp" serially +DROP INDEX timestamptzidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timestamptztmp WHERE a < '2018-12-18 10:59:54 GMT+3'::timestamptz; count diff --git a/contrib/btree_gist/expected/timetz.out b/contrib/btree_gist/expected/timetz.out index 7f73e44797..2eb70af8b1 100644 --- a/contrib/btree_gist/expected/timetz.out +++ b/contrib/btree_gist/expected/timetz.out @@ -18,7 +18,14 @@ INSERT INTO timetzcmp (r_id,a) SELECT 22,count(*) FROM timetztmp WHERE a <= '07: INSERT INTO timetzcmp (r_id,a) SELECT 23,count(*) FROM timetztmp WHERE a = '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 24,count(*) FROM timetztmp WHERE a >= '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07:46:45 GMT+4'; +SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); +DEBUG: building index "timetzidx" on table "timetztmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timetzidx_b" on table "timetztmp" serially +DROP INDEX timetzidx_b; +RESET client_min_messages; SET enable_seqscan=off; UPDATE timetzcmp SET b=c FROM ( SELECT count(*) AS c FROM timetztmp WHERE a < '07:46:45 GMT+3'::timetz ) q WHERE r_id=1 ; UPDATE timetzcmp SET b=c FROM ( SELECT count(*) AS c FROM timetztmp WHERE a <= '07:46:45 GMT+3'::timetz ) q WHERE r_id=2 ; diff --git a/contrib/btree_gist/expected/uuid.out b/contrib/btree_gist/expected/uuid.out index a34b024603..112d70055a 100644 --- a/contrib/btree_gist/expected/uuid.out +++ b/contrib/btree_gist/expected/uuid.out @@ -32,7 +32,14 @@ SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; 375 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); +DEBUG: building index "uuididx" on table "uuidtmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "uuididx_b" on table "uuidtmp" serially +DROP INDEX uuididx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; count diff --git a/contrib/btree_gist/expected/varbit.out b/contrib/btree_gist/expected/varbit.out index ede36bc3ea..d64d0ee6b4 100644 --- a/contrib/btree_gist/expected/varbit.out +++ b/contrib/btree_gist/expected/varbit.out @@ -32,7 +32,14 @@ SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; 50 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); +DEBUG: building index "varbitidx" on table "varbittmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "varbitidx_b" on table "varbittmp" serially +DROP INDEX varbitidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM varbittmp WHERE a < '1110100111010'::varbit; count diff --git a/contrib/btree_gist/expected/varchar.out b/contrib/btree_gist/expected/varchar.out index d071d714cd..9f4f95616a 100644 --- a/contrib/btree_gist/expected/varchar.out +++ b/contrib/btree_gist/expected/varchar.out @@ -32,7 +32,14 @@ SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); +DEBUG: building index "vcharidx" on table "vchartmp" serially +DEBUG: Using sorted GiST build +CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); +DEBUG: building index "vcharidx_b" on table "vchartmp" serially +DROP INDEX vcharidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM vchartmp WHERE a < '31b0'::varchar(32); count diff --git a/contrib/btree_gist/sql/bit.sql b/contrib/btree_gist/sql/bit.sql index a733042023..53c67cf77a 100644 --- a/contrib/btree_gist/sql/bit.sql +++ b/contrib/btree_gist/sql/bit.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM bittmp WHERE a >= '011011000100010111011000110000100'; SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; +SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); +CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX bitidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/bytea.sql b/contrib/btree_gist/sql/bytea.sql index 6885f5e56d..fdfa0c345b 100644 --- a/contrib/btree_gist/sql/bytea.sql +++ b/contrib/btree_gist/sql/bytea.sql @@ -17,7 +17,11 @@ SELECT count(*) FROM byteatmp WHERE a >= '31b0'; SELECT count(*) FROM byteatmp WHERE a > '31b0'; +SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); +CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX byteaidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/cash.sql b/contrib/btree_gist/sql/cash.sql index 4526cc4f0a..0581b3593e 100644 --- a/contrib/btree_gist/sql/cash.sql +++ b/contrib/btree_gist/sql/cash.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM moneytmp WHERE a > '22649.64'; SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); +CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); +DROP INDEX moneyidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/char.sql b/contrib/btree_gist/sql/char.sql index f6eb52e672..234eabee3b 100644 --- a/contrib/btree_gist/sql/char.sql +++ b/contrib/btree_gist/sql/char.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM chartmp WHERE a >= '31b0'::char(32); SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); +SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); +CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX charidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/cidr.sql b/contrib/btree_gist/sql/cidr.sql index 9bd77185b9..be2d22b079 100644 --- a/contrib/btree_gist/sql/cidr.sql +++ b/contrib/btree_gist/sql/cidr.sql @@ -15,7 +15,11 @@ SELECT count(*) FROM cidrtmp WHERE a >= '121.111.63.82'; SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; +SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); +CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX cidridx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/date.sql b/contrib/btree_gist/sql/date.sql index f969ef0a08..f007402bac 100644 --- a/contrib/btree_gist/sql/date.sql +++ b/contrib/btree_gist/sql/date.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM datetmp WHERE a > '2001-02-13'; SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); +CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); +DROP INDEX dateidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/enum.sql b/contrib/btree_gist/sql/enum.sql index 476211e979..d6dbcb4239 100644 --- a/contrib/btree_gist/sql/enum.sql +++ b/contrib/btree_gist/sql/enum.sql @@ -20,7 +20,11 @@ SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; +SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); +CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX enumidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/float4.sql b/contrib/btree_gist/sql/float4.sql index 3da1ce953c..0e3eb49343 100644 --- a/contrib/btree_gist/sql/float4.sql +++ b/contrib/btree_gist/sql/float4.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM float4tmp WHERE a > -179.0; SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); +CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX float4idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/float8.sql b/contrib/btree_gist/sql/float8.sql index e1e819b37f..6a216dd606 100644 --- a/contrib/btree_gist/sql/float8.sql +++ b/contrib/btree_gist/sql/float8.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM float8tmp WHERE a > -1890.0; SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); +CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX float8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/inet.sql b/contrib/btree_gist/sql/inet.sql index 4b8d354b00..0339c853d3 100644 --- a/contrib/btree_gist/sql/inet.sql +++ b/contrib/btree_gist/sql/inet.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM inettmp WHERE a >= '89.225.196.191'; SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; +SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); +CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); +DROP INDEX inetidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int2.sql b/contrib/btree_gist/sql/int2.sql index 988518795f..bf98ac65f8 100644 --- a/contrib/btree_gist/sql/int2.sql +++ b/contrib/btree_gist/sql/int2.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int2tmp WHERE a > 237; SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); +CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int2idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int4.sql b/contrib/btree_gist/sql/int4.sql index 659ab5ee24..214993314a 100644 --- a/contrib/btree_gist/sql/int4.sql +++ b/contrib/btree_gist/sql/int4.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int4tmp WHERE a > 237; SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); +CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int4idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int8.sql b/contrib/btree_gist/sql/int8.sql index 51e55e9c14..8a6c2a4bfd 100644 --- a/contrib/btree_gist/sql/int8.sql +++ b/contrib/btree_gist/sql/int8.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int8tmp WHERE a > 464571291354841; SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); +CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/interval.sql b/contrib/btree_gist/sql/interval.sql index 346d6adcb5..6f9b1d4a39 100644 --- a/contrib/btree_gist/sql/interval.sql +++ b/contrib/btree_gist/sql/interval.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM intervaltmp WHERE a > '199 days 21:21:23'; SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); +CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); +DROP INDEX intervalidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/macaddr.sql b/contrib/btree_gist/sql/macaddr.sql index 85c271f7ce..bccfc820ca 100644 --- a/contrib/btree_gist/sql/macaddr.sql +++ b/contrib/btree_gist/sql/macaddr.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM macaddrtmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; +SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); +CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX macaddridx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/macaddr8.sql b/contrib/btree_gist/sql/macaddr8.sql index 61e7d7af40..2d0447a777 100644 --- a/contrib/btree_gist/sql/macaddr8.sql +++ b/contrib/btree_gist/sql/macaddr8.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM macaddr8tmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; +SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); +CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX macaddr8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/numeric.sql b/contrib/btree_gist/sql/numeric.sql index dbb2f2f183..55ecbcdadc 100644 --- a/contrib/btree_gist/sql/numeric.sql +++ b/contrib/btree_gist/sql/numeric.sql @@ -40,7 +40,11 @@ SELECT count(*) FROM numerictmp WHERE a >= 0 ; SELECT count(*) FROM numerictmp WHERE a > 0 ; +SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); +CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); +DROP INDEX numericidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/oid.sql b/contrib/btree_gist/sql/oid.sql index c9358234ce..bc9ee0cba3 100644 --- a/contrib/btree_gist/sql/oid.sql +++ b/contrib/btree_gist/sql/oid.sql @@ -15,7 +15,11 @@ SELECT count(*) FROM oidtmp WHERE oid >= 17; SELECT count(*) FROM oidtmp WHERE oid > 17; +SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); +CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); +DROP INDEX oididx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/text.sql b/contrib/btree_gist/sql/text.sql index 46597e731d..52705a216d 100644 --- a/contrib/btree_gist/sql/text.sql +++ b/contrib/btree_gist/sql/text.sql @@ -17,7 +17,11 @@ SELECT count(*) FROM texttmp WHERE a >= '31b0'; SELECT count(*) FROM texttmp WHERE a > '31b0'; +SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); +CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX textidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/time.sql b/contrib/btree_gist/sql/time.sql index 6104e7f61c..6123945213 100644 --- a/contrib/btree_gist/sql/time.sql +++ b/contrib/btree_gist/sql/time.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM timetmp WHERE a > '10:57:11'; SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); +CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timeidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timestamp.sql b/contrib/btree_gist/sql/timestamp.sql index 95effebfc4..66a14f5ae5 100644 --- a/contrib/btree_gist/sql/timestamp.sql +++ b/contrib/btree_gist/sql/timestamp.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM timestamptmp WHERE a > '2004-10-26 08:55:08'; SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); +CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timestampidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timestamptz.sql b/contrib/btree_gist/sql/timestamptz.sql index f70caa4a64..2a92f63fc4 100644 --- a/contrib/btree_gist/sql/timestamptz.sql +++ b/contrib/btree_gist/sql/timestamptz.sql @@ -39,7 +39,11 @@ SELECT count(*) FROM timestamptztmp WHERE a > '2018-12-18 10:59:54 GMT+4'; SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); +CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timestamptzidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timetz.sql b/contrib/btree_gist/sql/timetz.sql index 2fb725db74..bc79d134b8 100644 --- a/contrib/btree_gist/sql/timetz.sql +++ b/contrib/btree_gist/sql/timetz.sql @@ -42,7 +42,11 @@ INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07: +SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); +CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timetzidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/uuid.sql b/contrib/btree_gist/sql/uuid.sql index 3f7ad764e2..7771bc0d82 100644 --- a/contrib/btree_gist/sql/uuid.sql +++ b/contrib/btree_gist/sql/uuid.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; +SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); +CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX uuididx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/varbit.sql b/contrib/btree_gist/sql/varbit.sql index e2a33b5a1b..6d8243572b 100644 --- a/contrib/btree_gist/sql/varbit.sql +++ b/contrib/btree_gist/sql/varbit.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM varbittmp WHERE a >= '1110100111010'; SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; +SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); +CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX varbitidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/varchar.sql b/contrib/btree_gist/sql/varchar.sql index 8087a17704..59b77e0983 100644 --- a/contrib/btree_gist/sql/varchar.sql +++ b/contrib/btree_gist/sql/varchar.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM vchartmp WHERE a >= '31b0'::varchar(32); SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); +SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); +CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); +DROP INDEX vcharidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index 1054f6f1f2..63a1a36947 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -257,6 +257,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) if (buildstate.buildMode == GIST_SORTED_BUILD) { + elog(DEBUG1, "Using sorted GiST build"); /* * Sort all data, build the index from bottom up. */ -- 2.24.3 (Apple Git-128) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-04-07 06:00  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 2 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2021-04-07 06:00 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>

On 08/03/2021 19:06, Andrey Borodin wrote:
> There were numerous GiST-build-related patches in this thread. Yet uncommitted is a patch with sortsupport routines for btree_gist contrib module.
> Here's its version which needs review.

Reviewing this now again. One thing caught my eye:

> +static int
> +gbt_bit_sort_build_cmp(Datum a, Datum b, SortSupport ssup)
> +{
> +	return DatumGetInt32(DirectFunctionCall2(byteacmp,
> +											 PointerGetDatum(a),
> +											 PointerGetDatum(b)));
> +}

That doesn't quite match the sort order used by the comparison 
functions, gbt_bitlt and such. The comparison functions compare the bits 
first, and use the length as a tie-breaker. Using byteacmp() will 
compare the "bit length" first. However, gbt_bitcmp() also uses 
byteacmp(), so I'm a bit confused. So, huh?

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-04-07 10:23  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  1 sibling, 0 replies; 111+ messages in thread

From: Heikki Linnakangas @ 2021-04-07 10:23 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>

On 07/04/2021 09:00, Heikki Linnakangas wrote:
> On 08/03/2021 19:06, Andrey Borodin wrote:
>> There were numerous GiST-build-related patches in this thread. Yet uncommitted is a patch with sortsupport routines for btree_gist contrib module.
>> Here's its version which needs review.

Committed with small fixes. I changed the all functions to use 
*GetDatum() and DatumGet*() macros, instead of just comparing Datums 
with < and >. Datum is unsigned, while int2, int4, int8 and money are 
signed, so that changes the sort order around 0 for those types to be 
the same as the picksplit and picksplit functions use. Not a correctness 
issue, the sorting only affects the quality of the index, but let's be tidy.

This issue remains:

> Reviewing this now again. One thing caught my eye:
> 
>> +static int
>> +gbt_bit_sort_build_cmp(Datum a, Datum b, SortSupport ssup)
>> +{
>> +	return DatumGetInt32(DirectFunctionCall2(byteacmp,
>> +											 PointerGetDatum(a),
>> +											 PointerGetDatum(b)));
>> +}
> 
> That doesn't quite match the sort order used by the comparison
> functions, gbt_bitlt and such. The comparison functions compare the bits
> first, and use the length as a tie-breaker. Using byteacmp() will
> compare the "bit length" first. However, gbt_bitcmp() also uses
> byteacmp(), so I'm a bit confused. So, huh?

Since we used byteacmp() previously for picksplit, too, this is 
consistent with the order you got previously. It still seems wrong to me 
and should be investigated, but it doesn't need to block this patch.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-04-07 11:56  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  1 sibling, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2021-04-07 11:56 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>

On 07/04/2021 09:00, Heikki Linnakangas wrote:
> On 08/03/2021 19:06, Andrey Borodin wrote:
>> There were numerous GiST-build-related patches in this thread. Yet uncommitted is a patch with sortsupport routines for btree_gist contrib module.
>> Here's its version which needs review.
> 
> Reviewing this now again. One thing caught my eye:
> 
>> +static int
>> +gbt_bit_sort_build_cmp(Datum a, Datum b, SortSupport ssup)
>> +{
>> +	return DatumGetInt32(DirectFunctionCall2(byteacmp,
>> +											 PointerGetDatum(a),
>> +											 PointerGetDatum(b)));
>> +}
> 
> That doesn't quite match the sort order used by the comparison
> functions, gbt_bitlt and such. The comparison functions compare the bits
> first, and use the length as a tie-breaker. Using byteacmp() will
> compare the "bit length" first. However, gbt_bitcmp() also uses
> byteacmp(), so I'm a bit confused. So, huh?

Ok, I think I understand that now. In btree_gist, the *_cmp() function 
operates on non-leaf values, and *_lt(), *_gt() et al operate on leaf 
values. For all other datatypes, the leaf and non-leaf representation is 
the same, but for bit/varbit, the non-leaf representation is different. 
The leaf representation is VarBit, and non-leaf is just the bits without 
the 'bit_len' field. That's why it is indeed correct for gbt_bitcmp() to 
just use byteacmp(), whereas gbt_bitlt() et al compares the 'bit_len' 
field separately. That's subtle, and 100% uncommented.

What that means for this patch is that gbt_bit_sort_build_cmp() should 
*not* call byteacmp(), but bitcmp(). Because it operates on the original 
datatype stored in the table.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-04-07 12:12  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2021-04-07 12:12 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>



> 7 апр. 2021 г., в 13:23, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> Committed with small fixes.

Thanks!

> 7 апр. 2021 г., в 14:56, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 
> Ok, I think I understand that now. In btree_gist, the *_cmp() function operates on non-leaf values, and *_lt(), *_gt() et al operate on leaf values. For all other datatypes, the leaf and non-leaf representation is the same, but for bit/varbit, the non-leaf representation is different. The leaf representation is VarBit, and non-leaf is just the bits without the 'bit_len' field. That's why it is indeed correct for gbt_bitcmp() to just use byteacmp(), whereas gbt_bitlt() et al compares the 'bit_len' field separately. That's subtle, and 100% uncommented.
> 
> What that means for this patch is that gbt_bit_sort_build_cmp() should *not* call byteacmp(), but bitcmp(). Because it operates on the original datatype stored in the table.

+1
Thanks for investigating this.
If I understand things right, adding test values with different lengths of bit sequences would not uncover the problem anyway?

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-04-07 13:18  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2021-04-07 13:18 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>

On 07/04/2021 15:12, Andrey Borodin wrote:
>> 7 апр. 2021 г., в 14:56, Heikki Linnakangas <hlinnaka@iki.fi>
>> написал(а):
>> 
>> Ok, I think I understand that now. In btree_gist, the *_cmp()
>> function operates on non-leaf values, and *_lt(), *_gt() et al
>> operate on leaf values. For all other datatypes, the leaf and
>> non-leaf representation is the same, but for bit/varbit, the
>> non-leaf representation is different. The leaf representation is
>> VarBit, and non-leaf is just the bits without the 'bit_len' field.
>> That's why it is indeed correct for gbt_bitcmp() to just use
>> byteacmp(), whereas gbt_bitlt() et al compares the 'bit_len' field
>> separately. That's subtle, and 100% uncommented.
>> 
>> What that means for this patch is that gbt_bit_sort_build_cmp()
>> should *not* call byteacmp(), but bitcmp(). Because it operates on
>> the original datatype stored in the table.
> 
> +1 Thanks for investigating this. If I understand things right,
> adding test values with different lengths of bit sequences would not
> uncover the problem anyway?

That's right, the only consequence of a "wrong" sort order is that the 
quality of the tree suffers, and scans need to scan more pages 
unnecessarily.

I tried to investigate this by creating a varbit index with and without 
sorting, and compared them with pageinspect, but in quick testing, I 
wasn't able to find cases where the sorted version was badly ordered. I 
guess I didn't find the right data set yet.

- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-04-07 19:41  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2021-04-07 19:41 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>



> 7 апр. 2021 г., в 16:18, Heikki Linnakangas <hlinnaka@iki.fi> написал(а):
> 

I see there is a problem with "SET client_min_messages = DEBUG1;" on buildfarm. I think these checks were necessary to make sure test paths are triggered. When we know that code paths are tested, maybe we can omit checks?

Best regards, Andrey Borodin.




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-04-07 20:18  Heikki Linnakangas <hlinnaka@iki.fi>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 1 reply; 111+ messages in thread

From: Heikki Linnakangas @ 2021-04-07 20:18 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>

On 07/04/2021 22:41, Andrey Borodin wrote:
> I see there is a problem with "SET client_min_messages = DEBUG1;" on
> buildfarm. I think these checks were necessary to make sure test
> paths are triggered. When we know that code paths are tested, maybe
> we can omit checks?
Yeah. We don't have very reliable coverage of different GiST build 
methods, as noted earlier in this thread. But that's not this patch's fault.

I've been investigating the varbit issue, and realized that all the 
comparison functions in this patch for varlen datatypes are broken. The 
representation that the sortsupport function sees is the one that the 
'compress' function returns. The fixed-length versions got this right, 
but the varlen versions assume that the input is a Datum of the original 
datatype. It happens to not crash, because the representation returned 
by gbt_var_compress() is also a varlena, and all of the comparison 
functions tolerate the garbage inputs, but it's bogus. The correct 
pattern would be something like this (without the debugging NOTICE, of 
course):

> static int
> gbt_text_sort_build_cmp(Datum a, Datum b, SortSupport ssup)
> {
> 	GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a));
> 	GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b));
> 
> 	int x = DatumGetInt32(DirectFunctionCall2Coll(bttextcmp,
> 												 ssup->ssup_collation,
> 												 PointerGetDatum(a),
> 												 PointerGetDatum(b)));
> 	elog(NOTICE, "cmp: %s vs %s: %d",
> 		 TextDatumGetCString(ra.lower),
> 		 TextDatumGetCString(rb.lower),
> 		 x);
> 	return x;
> }
- Heikki





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-05-07 13:05  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Heikki Linnakangas <hlinnaka@iki.fi>
  0 siblings, 1 reply; 111+ messages in thread

From: Andrey Borodin @ 2021-05-07 13:05 UTC (permalink / raw)
  To: Heikki Linnakangas <hlinnaka@iki.fi>; +Cc: Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>

Thanks for the investigation, Heikki.

> 8 апр. 2021 г., в 01:18, Heikki Linnakangas <hlinnaka@iki.fi> написThe correct pattern would be something like this (without the debugging NOTICE, of course):
> 
>> static int
>> gbt_text_sort_build_cmp(Datum a, Datum b, SortSupport ssup)
>> {
>> 	GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a));
>> 	GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b));
>> 	int x = DatumGetInt32(DirectFunctionCall2Coll(bttextcmp,
>> 												 ssup->ssup_collation,
>> 												 PointerGetDatum(a),
>> 												 PointerGetDatum(b)));
>> 	elog(NOTICE, "cmp: %s vs %s: %d",
>> 		 TextDatumGetCString(ra.lower),
>> 		 TextDatumGetCString(rb.lower),
>> 		 x);
>> 	return x;
>> }
> 

In this pattern I flipped PointerGetDatum(a) to PointerGetDatum(ra.lower), because it seems to me correct. I've followed rule of thumb: every sort function must extract and user "lower" somehow. Though I suspect numeric a bit. Is it regular varlena?
PFA patchset with v6 intact + two fixes of discovered issues.

Thanks!

Best regards, Andrey Borodin.

Attachments: download all (3 files, .tar.gz)

v7-0001-Add-sortsupport-for-gist_btree-opclasses-for-fast.patch (application/octet-stream, 82.9K) download From deb094260e8d6cd3858e1dafd88458c7fbc5530a Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas <heikki.linnakangas@iki.fi> Date: Wed, 7 Apr 2021 13:22:05 +0300 Subject: [PATCH v7 1/3] Add sortsupport for gist_btree opclasses, for faster index builds. Commit 16fa9b2b30 introduced a faster way to build GiST indexes, by sorting all the data. This commit adds the sortsupport functions needed to make use of that feature for btree_gist. Author: Andrey Borodin Discussion: https://www.postgresql.org/message-id/2F3F7265-0D22-44DB-AD71-8554C743D943@yandex-team.ru --- contrib/btree_gist/Makefile | 2 +- contrib/btree_gist/btree_bit.c | 25 +++ contrib/btree_gist/btree_bytea.c | 26 ++- contrib/btree_gist/btree_cash.c | 80 +++++++++ contrib/btree_gist/btree_date.c | 27 +++ contrib/btree_gist/btree_enum.c | 70 ++++++++ contrib/btree_gist/btree_float4.c | 71 ++++++++ contrib/btree_gist/btree_float8.c | 77 +++++++++ contrib/btree_gist/btree_gist--1.6--1.7.sql | 182 ++++++++++++++++++++ contrib/btree_gist/btree_gist.control | 2 +- contrib/btree_gist/btree_gist.h | 1 + contrib/btree_gist/btree_inet.c | 77 +++++++++ contrib/btree_gist/btree_int2.c | 70 ++++++++ contrib/btree_gist/btree_int4.c | 70 ++++++++ contrib/btree_gist/btree_int8.c | 80 +++++++++ contrib/btree_gist/btree_interval.c | 27 +++ contrib/btree_gist/btree_macaddr.c | 78 +++++++++ contrib/btree_gist/btree_macaddr8.c | 78 +++++++++ contrib/btree_gist/btree_numeric.c | 29 ++++ contrib/btree_gist/btree_oid.c | 70 ++++++++ contrib/btree_gist/btree_text.c | 25 +++ contrib/btree_gist/btree_time.c | 27 +++ contrib/btree_gist/btree_ts.c | 27 +++ contrib/btree_gist/btree_uuid.c | 25 +++ contrib/btree_gist/expected/bit.out | 7 + contrib/btree_gist/expected/bytea.out | 7 + contrib/btree_gist/expected/cash.out | 7 + contrib/btree_gist/expected/char.out | 7 + contrib/btree_gist/expected/cidr.out | 7 + contrib/btree_gist/expected/date.out | 7 + contrib/btree_gist/expected/enum.out | 7 + contrib/btree_gist/expected/float4.out | 7 + contrib/btree_gist/expected/float8.out | 7 + contrib/btree_gist/expected/inet.out | 7 + contrib/btree_gist/expected/int2.out | 7 + contrib/btree_gist/expected/int4.out | 7 + contrib/btree_gist/expected/int8.out | 7 + contrib/btree_gist/expected/interval.out | 7 + contrib/btree_gist/expected/macaddr.out | 7 + contrib/btree_gist/expected/macaddr8.out | 7 + contrib/btree_gist/expected/numeric.out | 7 + contrib/btree_gist/expected/oid.out | 7 + contrib/btree_gist/expected/text.out | 7 + contrib/btree_gist/expected/time.out | 7 + contrib/btree_gist/expected/timestamp.out | 7 + contrib/btree_gist/expected/timestamptz.out | 7 + contrib/btree_gist/expected/timetz.out | 7 + contrib/btree_gist/expected/uuid.out | 7 + contrib/btree_gist/expected/varbit.out | 7 + contrib/btree_gist/expected/varchar.out | 7 + contrib/btree_gist/sql/bit.sql | 4 + contrib/btree_gist/sql/bytea.sql | 4 + contrib/btree_gist/sql/cash.sql | 4 + contrib/btree_gist/sql/char.sql | 4 + contrib/btree_gist/sql/cidr.sql | 4 + contrib/btree_gist/sql/date.sql | 4 + contrib/btree_gist/sql/enum.sql | 4 + contrib/btree_gist/sql/float4.sql | 4 + contrib/btree_gist/sql/float8.sql | 4 + contrib/btree_gist/sql/inet.sql | 4 + contrib/btree_gist/sql/int2.sql | 4 + contrib/btree_gist/sql/int4.sql | 4 + contrib/btree_gist/sql/int8.sql | 4 + contrib/btree_gist/sql/interval.sql | 4 + contrib/btree_gist/sql/macaddr.sql | 4 + contrib/btree_gist/sql/macaddr8.sql | 4 + contrib/btree_gist/sql/numeric.sql | 4 + contrib/btree_gist/sql/oid.sql | 4 + contrib/btree_gist/sql/text.sql | 4 + contrib/btree_gist/sql/time.sql | 4 + contrib/btree_gist/sql/timestamp.sql | 4 + contrib/btree_gist/sql/timestamptz.sql | 4 + contrib/btree_gist/sql/timetz.sql | 4 + contrib/btree_gist/sql/uuid.sql | 4 + contrib/btree_gist/sql/varbit.sql | 4 + contrib/btree_gist/sql/varchar.sql | 4 + src/backend/access/gist/gistbuild.c | 1 + 77 files changed, 1530 insertions(+), 3 deletions(-) create mode 100644 contrib/btree_gist/btree_gist--1.6--1.7.sql diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile index e92d974a1a..a1f818f71e 100644 --- a/contrib/btree_gist/Makefile +++ b/contrib/btree_gist/Makefile @@ -32,7 +32,7 @@ EXTENSION = btree_gist DATA = btree_gist--1.0--1.1.sql \ btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \ - btree_gist--1.5--1.6.sql + btree_gist--1.5--1.6.sql btree_gist--1.6--1.7.sql PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes" REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \ diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c index 2225244ded..61b2eecfd5 100644 --- a/contrib/btree_gist/btree_bit.c +++ b/contrib/btree_gist/btree_bit.c @@ -19,6 +19,7 @@ PG_FUNCTION_INFO_V1(gbt_bit_picksplit); PG_FUNCTION_INFO_V1(gbt_bit_consistent); PG_FUNCTION_INFO_V1(gbt_bit_penalty); PG_FUNCTION_INFO_V1(gbt_bit_same); +PG_FUNCTION_INFO_V1(gbt_bit_sortsupport); /* define for comparison */ @@ -209,3 +210,27 @@ gbt_bit_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bit_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + /* Use byteacmp(), like gbt_bitcmp() does */ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bit_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_bit_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_bytea.c b/contrib/btree_gist/btree_bytea.c index 6b005f0157..a2abfb7d7c 100644 --- a/contrib/btree_gist/btree_bytea.c +++ b/contrib/btree_gist/btree_bytea.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_bytea_picksplit); PG_FUNCTION_INFO_V1(gbt_bytea_consistent); PG_FUNCTION_INFO_V1(gbt_bytea_penalty); PG_FUNCTION_INFO_V1(gbt_bytea_same); +PG_FUNCTION_INFO_V1(gbt_bytea_sortsupport); /* define for comparison */ @@ -87,7 +88,7 @@ static const gbtree_vinfo tinfo = /************************************************** - * Text ops + * Bytea ops **************************************************/ @@ -168,3 +169,26 @@ gbt_bytea_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bytea_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bytea_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_bytea_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c index dfa23224b6..dbd72d3ea0 100644 --- a/contrib/btree_gist/btree_cash.c +++ b/contrib/btree_gist/btree_cash.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_cash_consistent); PG_FUNCTION_INFO_V1(gbt_cash_distance); PG_FUNCTION_INFO_V1(gbt_cash_penalty); PG_FUNCTION_INFO_V1(gbt_cash_same); +PG_FUNCTION_INFO_V1(gbt_cash_sortsupport); static bool gbt_cashgt(const void *a, const void *b, FmgrInfo *flinfo) @@ -216,3 +217,82 @@ gbt_cash_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_cash_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + cashKEY *ia = (cashKEY *) DatumGetPointer(a); + cashKEY *ib = (cashKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_cash_abbrev_convert(Datum original, SortSupport ssup) +{ + cashKEY *b1 = (cashKEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return Int64GetDatum(z); +#else + return Int32GetDatum(z >> 32); +#endif +} + +static int +gbt_cash_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + int64 a = DatumGetInt64(z1); + int64 b = DatumGetInt64(z2); +#else + int32 a = DatumGetInt32(z1); + int32 b = DatumGetInt32(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_cash_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_cash_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_cash_cmp_abbrev; + ssup->abbrev_converter = gbt_cash_abbrev_convert; + ssup->abbrev_abort = gbt_cash_abbrev_abort; + ssup->abbrev_full_comparator = gbt_cash_sort_build_cmp; + } + else + { + ssup->comparator = gbt_cash_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c index 455a265a49..3abb6e9c47 100644 --- a/contrib/btree_gist/btree_date.c +++ b/contrib/btree_gist/btree_date.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_date_consistent); PG_FUNCTION_INFO_V1(gbt_date_distance); PG_FUNCTION_INFO_V1(gbt_date_penalty); PG_FUNCTION_INFO_V1(gbt_date_same); +PG_FUNCTION_INFO_V1(gbt_date_sortsupport); static bool gbt_dategt(const void *a, const void *b, FmgrInfo *flinfo) @@ -257,3 +258,29 @@ gbt_date_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_date_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + dateKEY *ia = (dateKEY *) PointerGetDatum(a); + dateKEY *ib = (dateKEY *) PointerGetDatum(b); + + return DatumGetInt32(DirectFunctionCall2(date_cmp, + DateADTGetDatum(ia->lower), + DateADTGetDatum(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_date_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_date_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_enum.c b/contrib/btree_gist/btree_enum.c index d4dc38a38e..e8c5bc5ffe 100644 --- a/contrib/btree_gist/btree_enum.c +++ b/contrib/btree_gist/btree_enum.c @@ -26,6 +26,7 @@ PG_FUNCTION_INFO_V1(gbt_enum_picksplit); PG_FUNCTION_INFO_V1(gbt_enum_consistent); PG_FUNCTION_INFO_V1(gbt_enum_penalty); PG_FUNCTION_INFO_V1(gbt_enum_same); +PG_FUNCTION_INFO_V1(gbt_enum_sortsupport); static bool @@ -183,3 +184,72 @@ gbt_enum_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_enum_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_enum_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + + return ObjectIdGetDatum(b1->lower); +} + +static int +gbt_enum_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + Oid a = DatumGetObjectId(z1); + Oid b = DatumGetObjectId(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_enum_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_enum_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_enum_cmp_abbrev; + ssup->abbrev_converter = gbt_enum_abbrev_convert; + ssup->abbrev_abort = gbt_enum_abbrev_abort; + ssup->abbrev_full_comparator = gbt_enum_sort_build_cmp; + } + else + { + ssup->comparator = gbt_enum_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c index 3604c73313..016b2d3d68 100644 --- a/contrib/btree_gist/btree_float4.c +++ b/contrib/btree_gist/btree_float4.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float4_consistent); PG_FUNCTION_INFO_V1(gbt_float4_distance); PG_FUNCTION_INFO_V1(gbt_float4_penalty); PG_FUNCTION_INFO_V1(gbt_float4_same); +PG_FUNCTION_INFO_V1(gbt_float4_sortsupport); static bool gbt_float4gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -209,3 +210,73 @@ gbt_float4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + + +static int +gbt_float4_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + float4KEY *ia = (float4KEY *) DatumGetPointer(a); + float4KEY *ib = (float4KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float4_abbrev_convert(Datum original, SortSupport ssup) +{ + float4KEY *b1 = (float4KEY *) DatumGetPointer(original); + + return Float4GetDatum(b1->lower); +} + +static int +gbt_float4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + float4 a = DatumGetFloat4(z1); + float4 b = DatumGetFloat4(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_float4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float4_cmp_abbrev; + ssup->abbrev_converter = gbt_float4_abbrev_convert; + ssup->abbrev_abort = gbt_float4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float4_sort_build_cmp; + } + else + { + ssup->comparator = gbt_float4_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c index 10a5262aaa..bee1e4e05e 100644 --- a/contrib/btree_gist/btree_float8.c +++ b/contrib/btree_gist/btree_float8.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_float8_consistent); PG_FUNCTION_INFO_V1(gbt_float8_distance); PG_FUNCTION_INFO_V1(gbt_float8_penalty); PG_FUNCTION_INFO_V1(gbt_float8_same); +PG_FUNCTION_INFO_V1(gbt_float8_sortsupport); static bool @@ -216,3 +217,79 @@ gbt_float8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_float8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + float8KEY *ia = (float8KEY *) DatumGetPointer(a); + float8KEY *ib = (float8KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float8_abbrev_convert(Datum original, SortSupport ssup) +{ + float8KEY *b1 = (float8KEY *) DatumGetPointer(original); + float8 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return Float8GetDatum(z); +#else + return Float4GetDatum((float4) z); +#endif +} + +static int +gbt_float8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + float8 a = DatumGetFloat8(z1); + float8 b = DatumGetFloat8(z2); +#else + float4 a = DatumGetFloat4(z1); + float4 b = DatumGetFloat4(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +static bool +gbt_float8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float8_cmp_abbrev; + ssup->abbrev_converter = gbt_float8_abbrev_convert; + ssup->abbrev_abort = gbt_float8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_float8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_gist--1.6--1.7.sql b/contrib/btree_gist/btree_gist--1.6--1.7.sql new file mode 100644 index 0000000000..abb5b8b0f4 --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.6--1.7.sql @@ -0,0 +1,182 @@ +/* contrib/btree_gist/btree_gist--1.6--1.7.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.7'" to load this file. \quit + + +CREATE FUNCTION gbt_int8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD + FUNCTION 11 (int8, int8) gbt_int8_sortsupport (internal) ; + +CREATE FUNCTION gbt_int4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD + FUNCTION 11 (int4, int4) gbt_int4_sortsupport (internal) ; + +CREATE FUNCTION gbt_int2_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD + FUNCTION 11 (int2, int2) gbt_int2_sortsupport (internal) ; + +CREATE FUNCTION gbt_float8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD + FUNCTION 11 (float8, float8) gbt_float8_sortsupport (internal) ; + +CREATE FUNCTION gbt_float4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD + FUNCTION 11 (float4, float4) gbt_float4_sortsupport (internal) ; + +CREATE FUNCTION gbt_enum_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD + FUNCTION 11 (anyenum, anyenum) gbt_enum_sortsupport (internal) ; + +CREATE FUNCTION gbt_oid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD + FUNCTION 11 (oid, oid) gbt_oid_sortsupport (internal) ; + +CREATE FUNCTION gbt_cash_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD + FUNCTION 11 (money, money) gbt_cash_sortsupport (internal) ; + +CREATE FUNCTION gbt_inet_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD + FUNCTION 11 (inet, inet) gbt_inet_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD + FUNCTION 11 (cidr, cidr) gbt_inet_sortsupport (internal) ; + + +CREATE FUNCTION gbt_macad_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD + FUNCTION 11 (macaddr, macaddr) gbt_macad_sortsupport (internal) ; + +CREATE FUNCTION gbt_macad8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD + FUNCTION 11 (macaddr8, macaddr8) gbt_macad8_sortsupport (internal) ; + +CREATE FUNCTION gbt_numeric_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD + FUNCTION 11 (numeric, numeric) gbt_numeric_sortsupport (internal) ; + +CREATE FUNCTION gbt_uuid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD + FUNCTION 11 (uuid, uuid) gbt_uuid_sortsupport (internal) ; + +CREATE FUNCTION gbt_ts_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD + FUNCTION 11 (timestamp, timestamp) gbt_ts_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (timestamptz, timestamptz) gbt_ts_sortsupport (internal) ; + +CREATE FUNCTION gbt_text_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_text_ops USING gist ADD + FUNCTION 11 (text, text) gbt_text_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD + FUNCTION 11 (bpchar, bpchar) gbt_text_sortsupport (internal) ; + +CREATE FUNCTION gbt_time_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_time_ops USING gist ADD + FUNCTION 11 (time, time) gbt_time_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD + FUNCTION 11 (timetz, timetz) gbt_time_sortsupport (internal) ; + +CREATE FUNCTION gbt_bytea_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD + FUNCTION 11 (bytea, bytea) gbt_bytea_sortsupport (internal) ; + +CREATE FUNCTION gbt_date_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_date_ops USING gist ADD + FUNCTION 11 (date, date) gbt_date_sortsupport (internal) ; + +CREATE FUNCTION gbt_bit_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD + FUNCTION 11 (bit, bit) gbt_bit_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD + FUNCTION 11 (varbit, varbit) gbt_bit_sortsupport (internal) ; + +CREATE FUNCTION gbt_intv_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD + FUNCTION 11 (interval, interval) gbt_intv_sortsupport (internal) ; + diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control index e5c41fe8f3..fa9171a80a 100644 --- a/contrib/btree_gist/btree_gist.control +++ b/contrib/btree_gist/btree_gist.control @@ -1,6 +1,6 @@ # btree_gist extension comment = 'support for indexing common datatypes in GiST' -default_version = '1.6' +default_version = '1.7' module_pathname = '$libdir/btree_gist' relocatable = true trusted = true diff --git a/contrib/btree_gist/btree_gist.h b/contrib/btree_gist/btree_gist.h index 14c7c8ee19..35ad287ed3 100644 --- a/contrib/btree_gist/btree_gist.h +++ b/contrib/btree_gist/btree_gist.h @@ -6,6 +6,7 @@ #include "access/nbtree.h" #include "fmgr.h" +#include "utils/sortsupport.h" #define BtreeGistNotEqualStrategyNumber 6 diff --git a/contrib/btree_gist/btree_inet.c b/contrib/btree_gist/btree_inet.c index e4b3a946b2..88136128ce 100644 --- a/contrib/btree_gist/btree_inet.c +++ b/contrib/btree_gist/btree_inet.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_inet_picksplit); PG_FUNCTION_INFO_V1(gbt_inet_consistent); PG_FUNCTION_INFO_V1(gbt_inet_penalty); PG_FUNCTION_INFO_V1(gbt_inet_same); +PG_FUNCTION_INFO_V1(gbt_inet_sortsupport); static bool @@ -186,3 +187,79 @@ gbt_inet_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_inet_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + inetKEY *ia = (inetKEY *) DatumGetPointer(a); + inetKEY *ib = (inetKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_inet_abbrev_convert(Datum original, SortSupport ssup) +{ + inetKEY *b1 = (inetKEY *) DatumGetPointer(original); + double z = b1->lower; + +#if SIZEOF_DATUM == 8 + return Float8GetDatum(z); +#else + return Float4GetDatum((float4) z); +#endif +} + +static int +gbt_inet_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + float8 a = DatumGetFloat8(z1); + float8 b = DatumGetFloat8(z2); +#else + float4 a = DatumGetFloat4(z1); + float4 b = DatumGetFloat4(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +static bool +gbt_inet_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_inet_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_inet_cmp_abbrev; + ssup->abbrev_converter = gbt_inet_abbrev_convert; + ssup->abbrev_abort = gbt_inet_abbrev_abort; + ssup->abbrev_full_comparator = gbt_inet_sort_build_cmp; + } + else + { + ssup->comparator = gbt_inet_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c index a91b95ff39..38ca3e05da 100644 --- a/contrib/btree_gist/btree_int2.c +++ b/contrib/btree_gist/btree_int2.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int2_consistent); PG_FUNCTION_INFO_V1(gbt_int2_distance); PG_FUNCTION_INFO_V1(gbt_int2_penalty); PG_FUNCTION_INFO_V1(gbt_int2_same); +PG_FUNCTION_INFO_V1(gbt_int2_sortsupport); static bool gbt_int2gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -214,3 +215,72 @@ gbt_int2_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int2_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int16KEY *ia = (int16KEY *) DatumGetPointer(a); + int16KEY *ib = (int16KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int2_abbrev_convert(Datum original, SortSupport ssup) +{ + int16KEY *b1 = (int16KEY *) DatumGetPointer(original); + + return Int16GetDatum(b1->lower); +} + +static int +gbt_int2_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + int16 a = DatumGetInt16(z1); + int16 b = DatumGetInt16(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int2_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int2_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int2_cmp_abbrev; + ssup->abbrev_converter = gbt_int2_abbrev_convert; + ssup->abbrev_abort = gbt_int2_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int2_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int2_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c index 7ea98c478c..21bd01ed10 100644 --- a/contrib/btree_gist/btree_int4.c +++ b/contrib/btree_gist/btree_int4.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int4_consistent); PG_FUNCTION_INFO_V1(gbt_int4_distance); PG_FUNCTION_INFO_V1(gbt_int4_penalty); PG_FUNCTION_INFO_V1(gbt_int4_same); +PG_FUNCTION_INFO_V1(gbt_int4_sortsupport); static bool @@ -215,3 +216,72 @@ gbt_int4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int4_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int32KEY *ia = (int32KEY *) DatumGetPointer(a); + int32KEY *ib = (int32KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int4_abbrev_convert(Datum original, SortSupport ssup) +{ + int32KEY *b1 = (int32KEY *) DatumGetPointer(original); + + return Int32GetDatum(b1->lower); +} + +static int +gbt_int4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + int32 a = DatumGetInt32(z1); + int32 b = DatumGetInt32(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int4_cmp_abbrev; + ssup->abbrev_converter = gbt_int4_abbrev_convert; + ssup->abbrev_abort = gbt_int4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int4_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int4_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c index df2b0d174b..b6e7fe6874 100644 --- a/contrib/btree_gist/btree_int8.c +++ b/contrib/btree_gist/btree_int8.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int8_consistent); PG_FUNCTION_INFO_V1(gbt_int8_distance); PG_FUNCTION_INFO_V1(gbt_int8_penalty); PG_FUNCTION_INFO_V1(gbt_int8_same); +PG_FUNCTION_INFO_V1(gbt_int8_sortsupport); static bool @@ -215,3 +216,82 @@ gbt_int8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int64KEY *ia = (int64KEY *) DatumGetPointer(a); + int64KEY *ib = (int64KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int8_abbrev_convert(Datum original, SortSupport ssup) +{ + int64KEY *b1 = (int64KEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return Int64GetDatum(z); +#else + return Int32GetDatum(z >> 32); +#endif +} + +static int +gbt_int8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + int64 a = DatumGetInt64(z1); + int64 b = DatumGetInt64(z2); +#else + int32 a = DatumGetInt32(z1); + int32 b = DatumGetInt32(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int8_cmp_abbrev; + ssup->abbrev_converter = gbt_int8_abbrev_convert; + ssup->abbrev_abort = gbt_int8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c index a4b3b2b1e6..0041acd3dd 100644 --- a/contrib/btree_gist/btree_interval.c +++ b/contrib/btree_gist/btree_interval.c @@ -27,6 +27,7 @@ PG_FUNCTION_INFO_V1(gbt_intv_consistent); PG_FUNCTION_INFO_V1(gbt_intv_distance); PG_FUNCTION_INFO_V1(gbt_intv_penalty); PG_FUNCTION_INFO_V1(gbt_intv_same); +PG_FUNCTION_INFO_V1(gbt_intv_sortsupport); static bool @@ -297,3 +298,29 @@ gbt_intv_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_intv_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + intvKEY *ia = (intvKEY *) DatumGetPointer(a); + intvKEY *ib = (intvKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(interval_cmp, + IntervalPGetDatum(&ia->lower), + IntervalPGetDatum(&ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_intv_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_intv_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_macaddr.c b/contrib/btree_gist/btree_macaddr.c index 7f0e9e9c91..805148575d 100644 --- a/contrib/btree_gist/btree_macaddr.c +++ b/contrib/btree_gist/btree_macaddr.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad_picksplit); PG_FUNCTION_INFO_V1(gbt_macad_consistent); PG_FUNCTION_INFO_V1(gbt_macad_penalty); PG_FUNCTION_INFO_V1(gbt_macad_same); +PG_FUNCTION_INFO_V1(gbt_macad_sortsupport); static bool @@ -195,3 +196,80 @@ gbt_macad_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + macKEY *ma = (macKEY *) DatumGetPointer(a); + macKEY *mb = (macKEY *) DatumGetPointer(b); + uint64 ia = mac_2_uint64(&ma->lower); + uint64 ib = mac_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + return 0; + + return (ia > ib) ? 1 : -1; +} + +static Datum +gbt_macad_abbrev_convert(Datum original, SortSupport ssup) +{ + macKEY *b1 = (macKEY *) DatumGetPointer(original); + uint64 z = mac_2_uint64(&b1->lower); + +#if SIZEOF_DATUM == 8 + return UInt64GetDatum(z); +#else + /* use the high 32 bits of the 48-bit integer */ + return UInt32GetDatum(z >> 16); +#endif +} + +static int +gbt_macad_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + uint64 a = DatumGetUInt64(z1); + uint64 b = DatumGetUInt64(z2); +#else + uint32 a = DatumGetUInt32(z1); + uint32 b = DatumGetUInt32(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +static bool +gbt_macad_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad_cmp_abbrev; + ssup->abbrev_converter = gbt_macad_abbrev_convert; + ssup->abbrev_abort = gbt_macad_abbrev_abort; + ssup->abbrev_full_comparator = gbt_macad_sort_build_cmp; + } + else + { + ssup->comparator = gbt_macad_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_macaddr8.c b/contrib/btree_gist/btree_macaddr8.c index ab4bca5d50..a0514727e3 100644 --- a/contrib/btree_gist/btree_macaddr8.c +++ b/contrib/btree_gist/btree_macaddr8.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad8_picksplit); PG_FUNCTION_INFO_V1(gbt_macad8_consistent); PG_FUNCTION_INFO_V1(gbt_macad8_penalty); PG_FUNCTION_INFO_V1(gbt_macad8_same); +PG_FUNCTION_INFO_V1(gbt_macad8_sortsupport); static bool @@ -195,3 +196,80 @@ gbt_macad8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + mac8KEY *ma = (mac8KEY *) DatumGetPointer(a); + mac8KEY *mb = (mac8KEY *) DatumGetPointer(b); + uint64 ia = mac8_2_uint64(&ma->lower); + uint64 ib = mac8_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + return 0; + + return (ia > ib) ? 1 : -1; +} + +static Datum +gbt_macad8_abbrev_convert(Datum original, SortSupport ssup) +{ + mac8KEY *b1 = (mac8KEY *) DatumGetPointer(original); + uint64 z = mac8_2_uint64(&b1->lower); + +#if SIZEOF_DATUM == 8 + return UInt64GetDatum(z); +#else + /* use the high bits only */ + return UInt32GetDatum(z >> 32); +#endif +} + +static int +gbt_macad8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + uint64 a = DatumGetUInt64(z1); + uint64 b = DatumGetUInt64(z2); +#else + uint32 a = DatumGetUInt32(z1); + uint32 b = DatumGetUInt32(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +static bool +gbt_macad8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad8_cmp_abbrev; + ssup->abbrev_converter = gbt_macad8_abbrev_convert; + ssup->abbrev_abort = gbt_macad8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_macad8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_macad8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_numeric.c b/contrib/btree_gist/btree_numeric.c index 35e466cdd9..face4e2b3a 100644 --- a/contrib/btree_gist/btree_numeric.c +++ b/contrib/btree_gist/btree_numeric.c @@ -21,6 +21,7 @@ PG_FUNCTION_INFO_V1(gbt_numeric_picksplit); PG_FUNCTION_INFO_V1(gbt_numeric_consistent); PG_FUNCTION_INFO_V1(gbt_numeric_penalty); PG_FUNCTION_INFO_V1(gbt_numeric_same); +PG_FUNCTION_INFO_V1(gbt_numeric_sortsupport); /* define for comparison */ @@ -227,3 +228,31 @@ gbt_numeric_picksplit(PG_FUNCTION_ARGS) &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(v); } + +static int +gbt_numeric_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(numeric_cmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_numeric_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_numeric_sort_build_cmp; + + /* + * Numeric has abbreviation routines in numeric.c, but we don't try to use + * them here. Maybe later. + */ + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c index 3cc7d4245d..9b7c546aee 100644 --- a/contrib/btree_gist/btree_oid.c +++ b/contrib/btree_gist/btree_oid.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_oid_consistent); PG_FUNCTION_INFO_V1(gbt_oid_distance); PG_FUNCTION_INFO_V1(gbt_oid_penalty); PG_FUNCTION_INFO_V1(gbt_oid_same); +PG_FUNCTION_INFO_V1(gbt_oid_sortsupport); static bool @@ -215,3 +216,72 @@ gbt_oid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_oid_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_oid_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + + return ObjectIdGetDatum(b1->lower); +} + +static int +gbt_oid_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + Oid a = DatumGetObjectId(z1); + Oid b = DatumGetObjectId(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_oid_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_oid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_oid_cmp_abbrev; + ssup->abbrev_converter = gbt_oid_abbrev_convert; + ssup->abbrev_abort = gbt_oid_abbrev_abort; + ssup->abbrev_full_comparator = gbt_oid_sort_build_cmp; + } + else + { + ssup->comparator = gbt_oid_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 8019d11281..01b1bda2f6 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_text_consistent); PG_FUNCTION_INFO_V1(gbt_bpchar_consistent); PG_FUNCTION_INFO_V1(gbt_text_penalty); PG_FUNCTION_INFO_V1(gbt_text_same); +PG_FUNCTION_INFO_V1(gbt_text_sortsupport); /* define for comparison */ @@ -239,3 +240,27 @@ gbt_text_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_text_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2Coll(bttextcmp, + ssup->ssup_collation, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_text_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_text_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c index fd8774a2f0..c021f67514 100644 --- a/contrib/btree_gist/btree_time.c +++ b/contrib/btree_gist/btree_time.c @@ -28,6 +28,7 @@ PG_FUNCTION_INFO_V1(gbt_time_distance); PG_FUNCTION_INFO_V1(gbt_timetz_consistent); PG_FUNCTION_INFO_V1(gbt_time_penalty); PG_FUNCTION_INFO_V1(gbt_time_same); +PG_FUNCTION_INFO_V1(gbt_time_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -332,3 +333,29 @@ gbt_time_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_time_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + timeKEY *ia = (timeKEY *) DatumGetPointer(a); + timeKEY *ib = (timeKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(time_cmp, + TimeADTGetDatumFast(ia->lower), + TimeADTGetDatumFast(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_time_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_time_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c index 2671ba961c..c6ef0782d2 100644 --- a/contrib/btree_gist/btree_ts.c +++ b/contrib/btree_gist/btree_ts.c @@ -31,6 +31,7 @@ PG_FUNCTION_INFO_V1(gbt_tstz_consistent); PG_FUNCTION_INFO_V1(gbt_tstz_distance); PG_FUNCTION_INFO_V1(gbt_ts_penalty); PG_FUNCTION_INFO_V1(gbt_ts_same); +PG_FUNCTION_INFO_V1(gbt_ts_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -399,3 +400,29 @@ gbt_ts_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_ts_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + tsKEY *ia = (tsKEY *) DatumGetPointer(a); + tsKEY *ib = (tsKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(timestamp_cmp, + TimestampGetDatumFast(ia->lower), + TimestampGetDatumFast(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_ts_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_ts_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_uuid.c b/contrib/btree_gist/btree_uuid.c index b81875979a..c802bf95a9 100644 --- a/contrib/btree_gist/btree_uuid.c +++ b/contrib/btree_gist/btree_uuid.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_uuid_picksplit); PG_FUNCTION_INFO_V1(gbt_uuid_consistent); PG_FUNCTION_INFO_V1(gbt_uuid_penalty); PG_FUNCTION_INFO_V1(gbt_uuid_same); +PG_FUNCTION_INFO_V1(gbt_uuid_sortsupport); static int @@ -233,3 +234,27 @@ gbt_uuid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_uuid_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + uuidKEY *ua = (uuidKEY *) DatumGetPointer(a); + uuidKEY *ub = (uuidKEY *) DatumGetPointer(b); + + return uuid_internal_cmp(&ua->lower, &ub->lower); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_uuid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_uuid_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/expected/bit.out b/contrib/btree_gist/expected/bit.out index e57871f310..cb2297ce80 100644 --- a/contrib/btree_gist/expected/bit.out +++ b/contrib/btree_gist/expected/bit.out @@ -32,7 +32,14 @@ SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; 350 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); +DEBUG: building index "bitidx" on table "bittmp" serially +DEBUG: using sorted GiST build +CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "bitidx_b" on table "bittmp" serially +DROP INDEX bitidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM bittmp WHERE a < '011011000100010111011000110000100'; count diff --git a/contrib/btree_gist/expected/bytea.out b/contrib/btree_gist/expected/bytea.out index b9efa73c08..170b48e1db 100644 --- a/contrib/btree_gist/expected/bytea.out +++ b/contrib/btree_gist/expected/bytea.out @@ -33,7 +33,14 @@ SELECT count(*) FROM byteatmp WHERE a > '31b0'; 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); +DEBUG: building index "byteaidx" on table "byteatmp" serially +DEBUG: using sorted GiST build +CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "byteaidx_b" on table "byteatmp" serially +DROP INDEX byteaidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM byteatmp WHERE a < '31b0'::bytea; count diff --git a/contrib/btree_gist/expected/cash.out b/contrib/btree_gist/expected/cash.out index 7fbc735592..868af70b22 100644 --- a/contrib/btree_gist/expected/cash.out +++ b/contrib/btree_gist/expected/cash.out @@ -40,7 +40,14 @@ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; $21,915.01 | $442.22 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); +DEBUG: building index "moneyidx" on table "moneytmp" serially +DEBUG: using sorted GiST build +CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "moneyidx_b" on table "moneytmp" serially +DROP INDEX moneyidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM moneytmp WHERE a < '22649.64'::money; count diff --git a/contrib/btree_gist/expected/char.out b/contrib/btree_gist/expected/char.out index d715c045cc..97316cbb06 100644 --- a/contrib/btree_gist/expected/char.out +++ b/contrib/btree_gist/expected/char.out @@ -32,7 +32,14 @@ SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); +DEBUG: building index "charidx" on table "chartmp" serially +DEBUG: using sorted GiST build +CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "charidx_b" on table "chartmp" serially +DROP INDEX charidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM chartmp WHERE a < '31b0'::char(32); count diff --git a/contrib/btree_gist/expected/cidr.out b/contrib/btree_gist/expected/cidr.out index 6d0995add6..f15597c06a 100644 --- a/contrib/btree_gist/expected/cidr.out +++ b/contrib/btree_gist/expected/cidr.out @@ -32,7 +32,14 @@ SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; 309 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); +DEBUG: building index "cidridx" on table "cidrtmp" serially +DEBUG: using sorted GiST build +CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "cidridx_b" on table "cidrtmp" serially +DROP INDEX cidridx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM cidrtmp WHERE a < '121.111.63.82'::cidr; count diff --git a/contrib/btree_gist/expected/date.out b/contrib/btree_gist/expected/date.out index 5db864bb82..5c93d02209 100644 --- a/contrib/btree_gist/expected/date.out +++ b/contrib/btree_gist/expected/date.out @@ -40,7 +40,14 @@ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; 03-24-2001 | 39 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); +DEBUG: building index "dateidx" on table "datetmp" serially +DEBUG: using sorted GiST build +CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "dateidx_b" on table "datetmp" serially +DROP INDEX dateidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM datetmp WHERE a < '2001-02-13'::date; count diff --git a/contrib/btree_gist/expected/enum.out b/contrib/btree_gist/expected/enum.out index c4b769dd4b..d73ad33974 100644 --- a/contrib/btree_gist/expected/enum.out +++ b/contrib/btree_gist/expected/enum.out @@ -46,7 +46,14 @@ SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; 230 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); +DEBUG: building index "enumidx" on table "enumtmp" serially +DEBUG: using sorted GiST build +CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "enumidx_b" on table "enumtmp" serially +DROP INDEX enumidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow; count diff --git a/contrib/btree_gist/expected/float4.out b/contrib/btree_gist/expected/float4.out index dfe732049e..5f4f1aa4ec 100644 --- a/contrib/btree_gist/expected/float4.out +++ b/contrib/btree_gist/expected/float4.out @@ -40,7 +40,14 @@ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; -158.17741 | 20.822586 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); +DEBUG: building index "float4idx" on table "float4tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "float4idx_b" on table "float4tmp" serially +DROP INDEX float4idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM float4tmp WHERE a < -179.0::float4; count diff --git a/contrib/btree_gist/expected/float8.out b/contrib/btree_gist/expected/float8.out index ebd0ef3d68..4db0f7b828 100644 --- a/contrib/btree_gist/expected/float8.out +++ b/contrib/btree_gist/expected/float8.out @@ -40,7 +40,14 @@ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; -1769.73634 | 120.26366000000007 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); +DEBUG: building index "float8idx" on table "float8tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "float8idx_b" on table "float8tmp" serially +DROP INDEX float8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM float8tmp WHERE a < -1890.0::float8; count diff --git a/contrib/btree_gist/expected/inet.out b/contrib/btree_gist/expected/inet.out index c323d903da..0847d3b7d1 100644 --- a/contrib/btree_gist/expected/inet.out +++ b/contrib/btree_gist/expected/inet.out @@ -32,7 +32,14 @@ SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; 386 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); +DEBUG: building index "inetidx" on table "inettmp" serially +DEBUG: using sorted GiST build +CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "inetidx_b" on table "inettmp" serially +DROP INDEX inetidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM inettmp WHERE a < '89.225.196.191'::inet; count diff --git a/contrib/btree_gist/expected/int2.out b/contrib/btree_gist/expected/int2.out index 50a332939b..9ad06a8dce 100644 --- a/contrib/btree_gist/expected/int2.out +++ b/contrib/btree_gist/expected/int2.out @@ -40,7 +40,14 @@ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); +DEBUG: building index "int2idx" on table "int2tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int2idx_b" on table "int2tmp" serially +DROP INDEX int2idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int2tmp WHERE a < 237::int2; count diff --git a/contrib/btree_gist/expected/int4.out b/contrib/btree_gist/expected/int4.out index 6bbdc7c3f4..fdf143f32c 100644 --- a/contrib/btree_gist/expected/int4.out +++ b/contrib/btree_gist/expected/int4.out @@ -40,7 +40,14 @@ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); +DEBUG: building index "int4idx" on table "int4tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int4idx_b" on table "int4tmp" serially +DROP INDEX int4idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int4tmp WHERE a < 237::int4; count diff --git a/contrib/btree_gist/expected/int8.out b/contrib/btree_gist/expected/int8.out index eff77c26b5..532c4e5e70 100644 --- a/contrib/btree_gist/expected/int8.out +++ b/contrib/btree_gist/expected/int8.out @@ -40,7 +40,14 @@ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' 478227196042750 | 13655904687909 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); +DEBUG: building index "int8idx" on table "int8tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int8idx_b" on table "int8tmp" serially +DROP INDEX int8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int8tmp WHERE a < 464571291354841::int8; count diff --git a/contrib/btree_gist/expected/interval.out b/contrib/btree_gist/expected/interval.out index 4c3d494e4a..12d50fdf58 100644 --- a/contrib/btree_gist/expected/interval.out +++ b/contrib/btree_gist/expected/interval.out @@ -40,7 +40,14 @@ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21 @ 220 days 19 hours 5 mins 42 secs | @ 21 days -2 hours -15 mins -41 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); +DEBUG: building index "intervalidx" on table "intervaltmp" serially +DEBUG: using sorted GiST build +CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "intervalidx_b" on table "intervaltmp" serially +DROP INDEX intervalidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM intervaltmp WHERE a < '199 days 21:21:23'::interval; count diff --git a/contrib/btree_gist/expected/macaddr.out b/contrib/btree_gist/expected/macaddr.out index c0a4c6287f..9634000618 100644 --- a/contrib/btree_gist/expected/macaddr.out +++ b/contrib/btree_gist/expected/macaddr.out @@ -32,7 +32,14 @@ SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); +DEBUG: building index "macaddridx" on table "macaddrtmp" serially +DEBUG: using sorted GiST build +CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "macaddridx_b" on table "macaddrtmp" serially +DROP INDEX macaddridx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM macaddrtmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr; count diff --git a/contrib/btree_gist/expected/macaddr8.out b/contrib/btree_gist/expected/macaddr8.out index e5ec6a5dea..910223cd3b 100644 --- a/contrib/btree_gist/expected/macaddr8.out +++ b/contrib/btree_gist/expected/macaddr8.out @@ -32,7 +32,14 @@ SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); +DEBUG: building index "macaddr8idx" on table "macaddr8tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "macaddr8idx_b" on table "macaddr8tmp" serially +DROP INDEX macaddr8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM macaddr8tmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr8; count diff --git a/contrib/btree_gist/expected/numeric.out b/contrib/btree_gist/expected/numeric.out index ae839b8ec8..8dce480c30 100644 --- a/contrib/btree_gist/expected/numeric.out +++ b/contrib/btree_gist/expected/numeric.out @@ -94,7 +94,14 @@ SELECT count(*) FROM numerictmp WHERE a > 0 ; 576 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); +DEBUG: building index "numericidx" on table "numerictmp" serially +DEBUG: using sorted GiST build +CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "numericidx_b" on table "numerictmp" serially +DROP INDEX numericidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM numerictmp WHERE a < -1890.0; count diff --git a/contrib/btree_gist/expected/oid.out b/contrib/btree_gist/expected/oid.out index 776bbb1026..da27172609 100644 --- a/contrib/btree_gist/expected/oid.out +++ b/contrib/btree_gist/expected/oid.out @@ -32,7 +32,14 @@ SELECT count(*) FROM oidtmp WHERE oid > 17; 983 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); +DEBUG: building index "oididx" on table "oidtmp" serially +DEBUG: using sorted GiST build +CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); +DEBUG: building index "oididx_b" on table "oidtmp" serially +DROP INDEX oididx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM oidtmp WHERE oid < 17; count diff --git a/contrib/btree_gist/expected/text.out b/contrib/btree_gist/expected/text.out index bb4e2e62d1..2e760d1487 100644 --- a/contrib/btree_gist/expected/text.out +++ b/contrib/btree_gist/expected/text.out @@ -33,7 +33,14 @@ SELECT count(*) FROM texttmp WHERE a > '31b0'; 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); +DEBUG: building index "textidx" on table "texttmp" serially +DEBUG: using sorted GiST build +CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "textidx_b" on table "texttmp" serially +DROP INDEX textidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM texttmp WHERE a < '31b0'::text; count diff --git a/contrib/btree_gist/expected/time.out b/contrib/btree_gist/expected/time.out index ec95ef77c5..9b81e58ed4 100644 --- a/contrib/btree_gist/expected/time.out +++ b/contrib/btree_gist/expected/time.out @@ -40,7 +40,14 @@ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; 10:55:32 | @ 1 min 39 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); +DEBUG: building index "timeidx" on table "timetmp" serially +DEBUG: using sorted GiST build +CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timeidx_b" on table "timetmp" serially +DROP INDEX timeidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timetmp WHERE a < '10:57:11'::time; count diff --git a/contrib/btree_gist/expected/timestamp.out b/contrib/btree_gist/expected/timestamp.out index 0d94f2f245..8ea9897551 100644 --- a/contrib/btree_gist/expected/timestamp.out +++ b/contrib/btree_gist/expected/timestamp.out @@ -40,7 +40,14 @@ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10- Mon Nov 29 20:12:43 2004 | @ 34 days 11 hours 17 mins 35 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); +DEBUG: building index "timestampidx" on table "timestamptmp" serially +DEBUG: using sorted GiST build +CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timestampidx_b" on table "timestamptmp" serially +DROP INDEX timestampidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timestamptmp WHERE a < '2004-10-26 08:55:08'::timestamp; count diff --git a/contrib/btree_gist/expected/timestamptz.out b/contrib/btree_gist/expected/timestamptz.out index 75a15a4256..2ba0dcd7ed 100644 --- a/contrib/btree_gist/expected/timestamptz.out +++ b/contrib/btree_gist/expected/timestamptz.out @@ -100,7 +100,14 @@ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> ' Thu Jan 24 12:28:12 2019 PST | @ 37 days 7 hours 28 mins 18 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); +DEBUG: building index "timestamptzidx" on table "timestamptztmp" serially +DEBUG: using sorted GiST build +CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timestamptzidx_b" on table "timestamptztmp" serially +DROP INDEX timestamptzidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timestamptztmp WHERE a < '2018-12-18 10:59:54 GMT+3'::timestamptz; count diff --git a/contrib/btree_gist/expected/timetz.out b/contrib/btree_gist/expected/timetz.out index 7f73e44797..6c855bfcd6 100644 --- a/contrib/btree_gist/expected/timetz.out +++ b/contrib/btree_gist/expected/timetz.out @@ -18,7 +18,14 @@ INSERT INTO timetzcmp (r_id,a) SELECT 22,count(*) FROM timetztmp WHERE a <= '07: INSERT INTO timetzcmp (r_id,a) SELECT 23,count(*) FROM timetztmp WHERE a = '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 24,count(*) FROM timetztmp WHERE a >= '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07:46:45 GMT+4'; +SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); +DEBUG: building index "timetzidx" on table "timetztmp" serially +DEBUG: using sorted GiST build +CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timetzidx_b" on table "timetztmp" serially +DROP INDEX timetzidx_b; +RESET client_min_messages; SET enable_seqscan=off; UPDATE timetzcmp SET b=c FROM ( SELECT count(*) AS c FROM timetztmp WHERE a < '07:46:45 GMT+3'::timetz ) q WHERE r_id=1 ; UPDATE timetzcmp SET b=c FROM ( SELECT count(*) AS c FROM timetztmp WHERE a <= '07:46:45 GMT+3'::timetz ) q WHERE r_id=2 ; diff --git a/contrib/btree_gist/expected/uuid.out b/contrib/btree_gist/expected/uuid.out index a34b024603..0f0f296039 100644 --- a/contrib/btree_gist/expected/uuid.out +++ b/contrib/btree_gist/expected/uuid.out @@ -32,7 +32,14 @@ SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; 375 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); +DEBUG: building index "uuididx" on table "uuidtmp" serially +DEBUG: using sorted GiST build +CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "uuididx_b" on table "uuidtmp" serially +DROP INDEX uuididx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; count diff --git a/contrib/btree_gist/expected/varbit.out b/contrib/btree_gist/expected/varbit.out index ede36bc3ea..9cd41f4c9a 100644 --- a/contrib/btree_gist/expected/varbit.out +++ b/contrib/btree_gist/expected/varbit.out @@ -32,7 +32,14 @@ SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; 50 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); +DEBUG: building index "varbitidx" on table "varbittmp" serially +DEBUG: using sorted GiST build +CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "varbitidx_b" on table "varbittmp" serially +DROP INDEX varbitidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM varbittmp WHERE a < '1110100111010'::varbit; count diff --git a/contrib/btree_gist/expected/varchar.out b/contrib/btree_gist/expected/varchar.out index d071d714cd..0520eb4731 100644 --- a/contrib/btree_gist/expected/varchar.out +++ b/contrib/btree_gist/expected/varchar.out @@ -32,7 +32,14 @@ SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); +DEBUG: building index "vcharidx" on table "vchartmp" serially +DEBUG: using sorted GiST build +CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); +DEBUG: building index "vcharidx_b" on table "vchartmp" serially +DROP INDEX vcharidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM vchartmp WHERE a < '31b0'::varchar(32); count diff --git a/contrib/btree_gist/sql/bit.sql b/contrib/btree_gist/sql/bit.sql index a733042023..53c67cf77a 100644 --- a/contrib/btree_gist/sql/bit.sql +++ b/contrib/btree_gist/sql/bit.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM bittmp WHERE a >= '011011000100010111011000110000100'; SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; +SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); +CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX bitidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/bytea.sql b/contrib/btree_gist/sql/bytea.sql index 6885f5e56d..fdfa0c345b 100644 --- a/contrib/btree_gist/sql/bytea.sql +++ b/contrib/btree_gist/sql/bytea.sql @@ -17,7 +17,11 @@ SELECT count(*) FROM byteatmp WHERE a >= '31b0'; SELECT count(*) FROM byteatmp WHERE a > '31b0'; +SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); +CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX byteaidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/cash.sql b/contrib/btree_gist/sql/cash.sql index 4526cc4f0a..0581b3593e 100644 --- a/contrib/btree_gist/sql/cash.sql +++ b/contrib/btree_gist/sql/cash.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM moneytmp WHERE a > '22649.64'; SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); +CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); +DROP INDEX moneyidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/char.sql b/contrib/btree_gist/sql/char.sql index f6eb52e672..234eabee3b 100644 --- a/contrib/btree_gist/sql/char.sql +++ b/contrib/btree_gist/sql/char.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM chartmp WHERE a >= '31b0'::char(32); SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); +SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); +CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX charidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/cidr.sql b/contrib/btree_gist/sql/cidr.sql index 9bd77185b9..be2d22b079 100644 --- a/contrib/btree_gist/sql/cidr.sql +++ b/contrib/btree_gist/sql/cidr.sql @@ -15,7 +15,11 @@ SELECT count(*) FROM cidrtmp WHERE a >= '121.111.63.82'; SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; +SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); +CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX cidridx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/date.sql b/contrib/btree_gist/sql/date.sql index f969ef0a08..f007402bac 100644 --- a/contrib/btree_gist/sql/date.sql +++ b/contrib/btree_gist/sql/date.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM datetmp WHERE a > '2001-02-13'; SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); +CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); +DROP INDEX dateidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/enum.sql b/contrib/btree_gist/sql/enum.sql index 476211e979..d6dbcb4239 100644 --- a/contrib/btree_gist/sql/enum.sql +++ b/contrib/btree_gist/sql/enum.sql @@ -20,7 +20,11 @@ SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; +SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); +CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX enumidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/float4.sql b/contrib/btree_gist/sql/float4.sql index 3da1ce953c..0e3eb49343 100644 --- a/contrib/btree_gist/sql/float4.sql +++ b/contrib/btree_gist/sql/float4.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM float4tmp WHERE a > -179.0; SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); +CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX float4idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/float8.sql b/contrib/btree_gist/sql/float8.sql index e1e819b37f..6a216dd606 100644 --- a/contrib/btree_gist/sql/float8.sql +++ b/contrib/btree_gist/sql/float8.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM float8tmp WHERE a > -1890.0; SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); +CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX float8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/inet.sql b/contrib/btree_gist/sql/inet.sql index 4b8d354b00..0339c853d3 100644 --- a/contrib/btree_gist/sql/inet.sql +++ b/contrib/btree_gist/sql/inet.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM inettmp WHERE a >= '89.225.196.191'; SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; +SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); +CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); +DROP INDEX inetidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int2.sql b/contrib/btree_gist/sql/int2.sql index 988518795f..bf98ac65f8 100644 --- a/contrib/btree_gist/sql/int2.sql +++ b/contrib/btree_gist/sql/int2.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int2tmp WHERE a > 237; SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); +CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int2idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int4.sql b/contrib/btree_gist/sql/int4.sql index 659ab5ee24..214993314a 100644 --- a/contrib/btree_gist/sql/int4.sql +++ b/contrib/btree_gist/sql/int4.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int4tmp WHERE a > 237; SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); +CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int4idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int8.sql b/contrib/btree_gist/sql/int8.sql index 51e55e9c14..8a6c2a4bfd 100644 --- a/contrib/btree_gist/sql/int8.sql +++ b/contrib/btree_gist/sql/int8.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int8tmp WHERE a > 464571291354841; SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); +CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/interval.sql b/contrib/btree_gist/sql/interval.sql index 346d6adcb5..6f9b1d4a39 100644 --- a/contrib/btree_gist/sql/interval.sql +++ b/contrib/btree_gist/sql/interval.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM intervaltmp WHERE a > '199 days 21:21:23'; SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); +CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); +DROP INDEX intervalidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/macaddr.sql b/contrib/btree_gist/sql/macaddr.sql index 85c271f7ce..bccfc820ca 100644 --- a/contrib/btree_gist/sql/macaddr.sql +++ b/contrib/btree_gist/sql/macaddr.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM macaddrtmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; +SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); +CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX macaddridx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/macaddr8.sql b/contrib/btree_gist/sql/macaddr8.sql index 61e7d7af40..2d0447a777 100644 --- a/contrib/btree_gist/sql/macaddr8.sql +++ b/contrib/btree_gist/sql/macaddr8.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM macaddr8tmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; +SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); +CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX macaddr8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/numeric.sql b/contrib/btree_gist/sql/numeric.sql index dbb2f2f183..55ecbcdadc 100644 --- a/contrib/btree_gist/sql/numeric.sql +++ b/contrib/btree_gist/sql/numeric.sql @@ -40,7 +40,11 @@ SELECT count(*) FROM numerictmp WHERE a >= 0 ; SELECT count(*) FROM numerictmp WHERE a > 0 ; +SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); +CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); +DROP INDEX numericidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/oid.sql b/contrib/btree_gist/sql/oid.sql index c9358234ce..bc9ee0cba3 100644 --- a/contrib/btree_gist/sql/oid.sql +++ b/contrib/btree_gist/sql/oid.sql @@ -15,7 +15,11 @@ SELECT count(*) FROM oidtmp WHERE oid >= 17; SELECT count(*) FROM oidtmp WHERE oid > 17; +SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); +CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); +DROP INDEX oididx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/text.sql b/contrib/btree_gist/sql/text.sql index 46597e731d..52705a216d 100644 --- a/contrib/btree_gist/sql/text.sql +++ b/contrib/btree_gist/sql/text.sql @@ -17,7 +17,11 @@ SELECT count(*) FROM texttmp WHERE a >= '31b0'; SELECT count(*) FROM texttmp WHERE a > '31b0'; +SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); +CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX textidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/time.sql b/contrib/btree_gist/sql/time.sql index 6104e7f61c..6123945213 100644 --- a/contrib/btree_gist/sql/time.sql +++ b/contrib/btree_gist/sql/time.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM timetmp WHERE a > '10:57:11'; SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); +CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timeidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timestamp.sql b/contrib/btree_gist/sql/timestamp.sql index 95effebfc4..66a14f5ae5 100644 --- a/contrib/btree_gist/sql/timestamp.sql +++ b/contrib/btree_gist/sql/timestamp.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM timestamptmp WHERE a > '2004-10-26 08:55:08'; SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); +CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timestampidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timestamptz.sql b/contrib/btree_gist/sql/timestamptz.sql index f70caa4a64..2a92f63fc4 100644 --- a/contrib/btree_gist/sql/timestamptz.sql +++ b/contrib/btree_gist/sql/timestamptz.sql @@ -39,7 +39,11 @@ SELECT count(*) FROM timestamptztmp WHERE a > '2018-12-18 10:59:54 GMT+4'; SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); +CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timestamptzidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timetz.sql b/contrib/btree_gist/sql/timetz.sql index 2fb725db74..bc79d134b8 100644 --- a/contrib/btree_gist/sql/timetz.sql +++ b/contrib/btree_gist/sql/timetz.sql @@ -42,7 +42,11 @@ INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07: +SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); +CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timetzidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/uuid.sql b/contrib/btree_gist/sql/uuid.sql index 3f7ad764e2..7771bc0d82 100644 --- a/contrib/btree_gist/sql/uuid.sql +++ b/contrib/btree_gist/sql/uuid.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; +SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); +CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX uuididx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/varbit.sql b/contrib/btree_gist/sql/varbit.sql index e2a33b5a1b..6d8243572b 100644 --- a/contrib/btree_gist/sql/varbit.sql +++ b/contrib/btree_gist/sql/varbit.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM varbittmp WHERE a >= '1110100111010'; SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; +SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); +CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX varbitidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/varchar.sql b/contrib/btree_gist/sql/varchar.sql index 8087a17704..59b77e0983 100644 --- a/contrib/btree_gist/sql/varchar.sql +++ b/contrib/btree_gist/sql/varchar.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM vchartmp WHERE a >= '31b0'::varchar(32); SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); +SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); +CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); +DROP INDEX vcharidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index f46a42197c..2b6bc8d7a1 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -260,6 +260,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) /* * Sort all data, build the index from bottom up. */ + elog(DEBUG1, "using sorted GiST build"); buildstate.sortstate = tuplesort_begin_index_gist(heap, index, maintenance_work_mem, -- 2.24.3 (Apple Git-128) =
v7-0002-Remove-DEBUG1-verification-of-the-fact-that-index.patch (application/octet-stream, 35.7K) download From 444fc4a6edaaa1197740bebdc2b842244f191637 Mon Sep 17 00:00:00 2001 From: Andrey Borodin <amborodin@acm.org> Date: Fri, 7 May 2021 15:24:40 +0500 Subject: [PATCH v7 2/3] Remove DEBUG1 verification of the fact that index was built with sorting --- contrib/btree_gist/expected/bit.out | 4 ---- contrib/btree_gist/expected/bytea.out | 4 ---- contrib/btree_gist/expected/cash.out | 4 ---- contrib/btree_gist/expected/char.out | 4 ---- contrib/btree_gist/expected/cidr.out | 4 ---- contrib/btree_gist/expected/date.out | 4 ---- contrib/btree_gist/expected/enum.out | 4 ---- contrib/btree_gist/expected/float4.out | 4 ---- contrib/btree_gist/expected/float8.out | 4 ---- contrib/btree_gist/expected/inet.out | 4 ---- contrib/btree_gist/expected/int2.out | 4 ---- contrib/btree_gist/expected/int4.out | 4 ---- contrib/btree_gist/expected/int8.out | 4 ---- contrib/btree_gist/expected/interval.out | 4 ---- contrib/btree_gist/expected/macaddr.out | 4 ---- contrib/btree_gist/expected/macaddr8.out | 4 ---- contrib/btree_gist/expected/numeric.out | 4 ---- contrib/btree_gist/expected/oid.out | 4 ---- contrib/btree_gist/expected/text.out | 4 ---- contrib/btree_gist/expected/time.out | 4 ---- contrib/btree_gist/expected/timestamp.out | 4 ---- contrib/btree_gist/expected/timestamptz.out | 4 ---- contrib/btree_gist/expected/timetz.out | 4 ---- contrib/btree_gist/expected/uuid.out | 4 ---- contrib/btree_gist/expected/varbit.out | 4 ---- contrib/btree_gist/expected/varchar.out | 4 ---- contrib/btree_gist/sql/bit.sql | 1 - contrib/btree_gist/sql/bytea.sql | 1 - contrib/btree_gist/sql/cash.sql | 1 - contrib/btree_gist/sql/char.sql | 1 - contrib/btree_gist/sql/cidr.sql | 1 - contrib/btree_gist/sql/date.sql | 1 - contrib/btree_gist/sql/enum.sql | 1 - contrib/btree_gist/sql/float4.sql | 1 - contrib/btree_gist/sql/float8.sql | 1 - contrib/btree_gist/sql/inet.sql | 1 - contrib/btree_gist/sql/int2.sql | 1 - contrib/btree_gist/sql/int4.sql | 1 - contrib/btree_gist/sql/int8.sql | 1 - contrib/btree_gist/sql/interval.sql | 1 - contrib/btree_gist/sql/macaddr.sql | 1 - contrib/btree_gist/sql/macaddr8.sql | 1 - contrib/btree_gist/sql/numeric.sql | 1 - contrib/btree_gist/sql/oid.sql | 1 - contrib/btree_gist/sql/text.sql | 1 - contrib/btree_gist/sql/time.sql | 1 - contrib/btree_gist/sql/timestamp.sql | 1 - contrib/btree_gist/sql/timestamptz.sql | 1 - contrib/btree_gist/sql/timetz.sql | 2 -- contrib/btree_gist/sql/uuid.sql | 1 - contrib/btree_gist/sql/varbit.sql | 1 - contrib/btree_gist/sql/varchar.sql | 1 - 52 files changed, 131 deletions(-) diff --git a/contrib/btree_gist/expected/bit.out b/contrib/btree_gist/expected/bit.out index cb2297ce80..c3da8456d4 100644 --- a/contrib/btree_gist/expected/bit.out +++ b/contrib/btree_gist/expected/bit.out @@ -32,12 +32,8 @@ SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; 350 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); -DEBUG: building index "bitidx" on table "bittmp" serially -DEBUG: using sorted GiST build CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); -DEBUG: building index "bitidx_b" on table "bittmp" serially DROP INDEX bitidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/bytea.out b/contrib/btree_gist/expected/bytea.out index 170b48e1db..7f112236f3 100644 --- a/contrib/btree_gist/expected/bytea.out +++ b/contrib/btree_gist/expected/bytea.out @@ -33,12 +33,8 @@ SELECT count(*) FROM byteatmp WHERE a > '31b0'; 400 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); -DEBUG: building index "byteaidx" on table "byteatmp" serially -DEBUG: using sorted GiST build CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); -DEBUG: building index "byteaidx_b" on table "byteatmp" serially DROP INDEX byteaidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/cash.out b/contrib/btree_gist/expected/cash.out index 868af70b22..b8a2c71c5e 100644 --- a/contrib/btree_gist/expected/cash.out +++ b/contrib/btree_gist/expected/cash.out @@ -40,12 +40,8 @@ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; $21,915.01 | $442.22 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); -DEBUG: building index "moneyidx" on table "moneytmp" serially -DEBUG: using sorted GiST build CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "moneyidx_b" on table "moneytmp" serially DROP INDEX moneyidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/char.out b/contrib/btree_gist/expected/char.out index 97316cbb06..d4e5e9ad2d 100644 --- a/contrib/btree_gist/expected/char.out +++ b/contrib/btree_gist/expected/char.out @@ -32,12 +32,8 @@ SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); 400 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); -DEBUG: building index "charidx" on table "chartmp" serially -DEBUG: using sorted GiST build CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); -DEBUG: building index "charidx_b" on table "chartmp" serially DROP INDEX charidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/cidr.out b/contrib/btree_gist/expected/cidr.out index f15597c06a..782c174ea4 100644 --- a/contrib/btree_gist/expected/cidr.out +++ b/contrib/btree_gist/expected/cidr.out @@ -32,12 +32,8 @@ SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; 309 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); -DEBUG: building index "cidridx" on table "cidrtmp" serially -DEBUG: using sorted GiST build CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "cidridx_b" on table "cidrtmp" serially DROP INDEX cidridx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/date.out b/contrib/btree_gist/expected/date.out index 5c93d02209..3a5e931f1a 100644 --- a/contrib/btree_gist/expected/date.out +++ b/contrib/btree_gist/expected/date.out @@ -40,12 +40,8 @@ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; 03-24-2001 | 39 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); -DEBUG: building index "dateidx" on table "datetmp" serially -DEBUG: using sorted GiST build CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "dateidx_b" on table "datetmp" serially DROP INDEX dateidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/enum.out b/contrib/btree_gist/expected/enum.out index d73ad33974..fa7521a2e6 100644 --- a/contrib/btree_gist/expected/enum.out +++ b/contrib/btree_gist/expected/enum.out @@ -46,12 +46,8 @@ SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; 230 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); -DEBUG: building index "enumidx" on table "enumtmp" serially -DEBUG: using sorted GiST build CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "enumidx_b" on table "enumtmp" serially DROP INDEX enumidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/float4.out b/contrib/btree_gist/expected/float4.out index 5f4f1aa4ec..83a88077f2 100644 --- a/contrib/btree_gist/expected/float4.out +++ b/contrib/btree_gist/expected/float4.out @@ -40,12 +40,8 @@ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; -158.17741 | 20.822586 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); -DEBUG: building index "float4idx" on table "float4tmp" serially -DEBUG: using sorted GiST build CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "float4idx_b" on table "float4tmp" serially DROP INDEX float4idx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/float8.out b/contrib/btree_gist/expected/float8.out index 4db0f7b828..e29f1dea75 100644 --- a/contrib/btree_gist/expected/float8.out +++ b/contrib/btree_gist/expected/float8.out @@ -40,12 +40,8 @@ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; -1769.73634 | 120.26366000000007 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); -DEBUG: building index "float8idx" on table "float8tmp" serially -DEBUG: using sorted GiST build CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "float8idx_b" on table "float8tmp" serially DROP INDEX float8idx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/inet.out b/contrib/btree_gist/expected/inet.out index 0847d3b7d1..7ff1598634 100644 --- a/contrib/btree_gist/expected/inet.out +++ b/contrib/btree_gist/expected/inet.out @@ -32,12 +32,8 @@ SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; 386 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); -DEBUG: building index "inetidx" on table "inettmp" serially -DEBUG: using sorted GiST build CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "inetidx_b" on table "inettmp" serially DROP INDEX inetidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/int2.out b/contrib/btree_gist/expected/int2.out index 9ad06a8dce..f1337a3534 100644 --- a/contrib/btree_gist/expected/int2.out +++ b/contrib/btree_gist/expected/int2.out @@ -40,12 +40,8 @@ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); -DEBUG: building index "int2idx" on table "int2tmp" serially -DEBUG: using sorted GiST build CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "int2idx_b" on table "int2tmp" serially DROP INDEX int2idx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/int4.out b/contrib/btree_gist/expected/int4.out index fdf143f32c..425b0183cd 100644 --- a/contrib/btree_gist/expected/int4.out +++ b/contrib/btree_gist/expected/int4.out @@ -40,12 +40,8 @@ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); -DEBUG: building index "int4idx" on table "int4tmp" serially -DEBUG: using sorted GiST build CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "int4idx_b" on table "int4tmp" serially DROP INDEX int4idx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/int8.out b/contrib/btree_gist/expected/int8.out index 532c4e5e70..1e327b28e8 100644 --- a/contrib/btree_gist/expected/int8.out +++ b/contrib/btree_gist/expected/int8.out @@ -40,12 +40,8 @@ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' 478227196042750 | 13655904687909 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); -DEBUG: building index "int8idx" on table "int8tmp" serially -DEBUG: using sorted GiST build CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "int8idx_b" on table "int8tmp" serially DROP INDEX int8idx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/interval.out b/contrib/btree_gist/expected/interval.out index 12d50fdf58..b23d1591a6 100644 --- a/contrib/btree_gist/expected/interval.out +++ b/contrib/btree_gist/expected/interval.out @@ -40,12 +40,8 @@ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21 @ 220 days 19 hours 5 mins 42 secs | @ 21 days -2 hours -15 mins -41 secs (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); -DEBUG: building index "intervalidx" on table "intervaltmp" serially -DEBUG: using sorted GiST build CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "intervalidx_b" on table "intervaltmp" serially DROP INDEX intervalidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/macaddr.out b/contrib/btree_gist/expected/macaddr.out index 9634000618..06a8044257 100644 --- a/contrib/btree_gist/expected/macaddr.out +++ b/contrib/btree_gist/expected/macaddr.out @@ -32,12 +32,8 @@ SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); -DEBUG: building index "macaddridx" on table "macaddrtmp" serially -DEBUG: using sorted GiST build CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "macaddridx_b" on table "macaddrtmp" serially DROP INDEX macaddridx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/macaddr8.out b/contrib/btree_gist/expected/macaddr8.out index 910223cd3b..284d66cce3 100644 --- a/contrib/btree_gist/expected/macaddr8.out +++ b/contrib/btree_gist/expected/macaddr8.out @@ -32,12 +32,8 @@ SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); -DEBUG: building index "macaddr8idx" on table "macaddr8tmp" serially -DEBUG: using sorted GiST build CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "macaddr8idx_b" on table "macaddr8tmp" serially DROP INDEX macaddr8idx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/numeric.out b/contrib/btree_gist/expected/numeric.out index 8dce480c30..a3e2a896c5 100644 --- a/contrib/btree_gist/expected/numeric.out +++ b/contrib/btree_gist/expected/numeric.out @@ -94,12 +94,8 @@ SELECT count(*) FROM numerictmp WHERE a > 0 ; 576 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); -DEBUG: building index "numericidx" on table "numerictmp" serially -DEBUG: using sorted GiST build CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "numericidx_b" on table "numerictmp" serially DROP INDEX numericidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/oid.out b/contrib/btree_gist/expected/oid.out index da27172609..59ddef92f7 100644 --- a/contrib/btree_gist/expected/oid.out +++ b/contrib/btree_gist/expected/oid.out @@ -32,12 +32,8 @@ SELECT count(*) FROM oidtmp WHERE oid > 17; 983 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); -DEBUG: building index "oididx" on table "oidtmp" serially -DEBUG: using sorted GiST build CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); -DEBUG: building index "oididx_b" on table "oidtmp" serially DROP INDEX oididx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/text.out b/contrib/btree_gist/expected/text.out index 2e760d1487..223ffb2750 100644 --- a/contrib/btree_gist/expected/text.out +++ b/contrib/btree_gist/expected/text.out @@ -33,12 +33,8 @@ SELECT count(*) FROM texttmp WHERE a > '31b0'; 400 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); -DEBUG: building index "textidx" on table "texttmp" serially -DEBUG: using sorted GiST build CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); -DEBUG: building index "textidx_b" on table "texttmp" serially DROP INDEX textidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/time.out b/contrib/btree_gist/expected/time.out index 9b81e58ed4..0b972ac35a 100644 --- a/contrib/btree_gist/expected/time.out +++ b/contrib/btree_gist/expected/time.out @@ -40,12 +40,8 @@ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; 10:55:32 | @ 1 min 39 secs (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); -DEBUG: building index "timeidx" on table "timetmp" serially -DEBUG: using sorted GiST build CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "timeidx_b" on table "timetmp" serially DROP INDEX timeidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/timestamp.out b/contrib/btree_gist/expected/timestamp.out index 8ea9897551..b17f65f698 100644 --- a/contrib/btree_gist/expected/timestamp.out +++ b/contrib/btree_gist/expected/timestamp.out @@ -40,12 +40,8 @@ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10- Mon Nov 29 20:12:43 2004 | @ 34 days 11 hours 17 mins 35 secs (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); -DEBUG: building index "timestampidx" on table "timestamptmp" serially -DEBUG: using sorted GiST build CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "timestampidx_b" on table "timestamptmp" serially DROP INDEX timestampidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/timestamptz.out b/contrib/btree_gist/expected/timestamptz.out index 2ba0dcd7ed..5b098006cd 100644 --- a/contrib/btree_gist/expected/timestamptz.out +++ b/contrib/btree_gist/expected/timestamptz.out @@ -100,12 +100,8 @@ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> ' Thu Jan 24 12:28:12 2019 PST | @ 37 days 7 hours 28 mins 18 secs (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); -DEBUG: building index "timestamptzidx" on table "timestamptztmp" serially -DEBUG: using sorted GiST build CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "timestamptzidx_b" on table "timestamptztmp" serially DROP INDEX timestamptzidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/timetz.out b/contrib/btree_gist/expected/timetz.out index 6c855bfcd6..33d9b2ace3 100644 --- a/contrib/btree_gist/expected/timetz.out +++ b/contrib/btree_gist/expected/timetz.out @@ -18,12 +18,8 @@ INSERT INTO timetzcmp (r_id,a) SELECT 22,count(*) FROM timetztmp WHERE a <= '07: INSERT INTO timetzcmp (r_id,a) SELECT 23,count(*) FROM timetztmp WHERE a = '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 24,count(*) FROM timetztmp WHERE a >= '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07:46:45 GMT+4'; -SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); -DEBUG: building index "timetzidx" on table "timetztmp" serially -DEBUG: using sorted GiST build CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "timetzidx_b" on table "timetztmp" serially DROP INDEX timetzidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/uuid.out b/contrib/btree_gist/expected/uuid.out index 0f0f296039..3d292dce5a 100644 --- a/contrib/btree_gist/expected/uuid.out +++ b/contrib/btree_gist/expected/uuid.out @@ -32,12 +32,8 @@ SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; 375 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); -DEBUG: building index "uuididx" on table "uuidtmp" serially -DEBUG: using sorted GiST build CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "uuididx_b" on table "uuidtmp" serially DROP INDEX uuididx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/varbit.out b/contrib/btree_gist/expected/varbit.out index 9cd41f4c9a..1722e0077a 100644 --- a/contrib/btree_gist/expected/varbit.out +++ b/contrib/btree_gist/expected/varbit.out @@ -32,12 +32,8 @@ SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; 50 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); -DEBUG: building index "varbitidx" on table "varbittmp" serially -DEBUG: using sorted GiST build CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); -DEBUG: building index "varbitidx_b" on table "varbittmp" serially DROP INDEX varbitidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/expected/varchar.out b/contrib/btree_gist/expected/varchar.out index 0520eb4731..6085e5ec99 100644 --- a/contrib/btree_gist/expected/varchar.out +++ b/contrib/btree_gist/expected/varchar.out @@ -32,12 +32,8 @@ SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); 400 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); -DEBUG: building index "vcharidx" on table "vchartmp" serially -DEBUG: using sorted GiST build CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); -DEBUG: building index "vcharidx_b" on table "vchartmp" serially DROP INDEX vcharidx_b; RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/bit.sql b/contrib/btree_gist/sql/bit.sql index 53c67cf77a..f7f72354fe 100644 --- a/contrib/btree_gist/sql/bit.sql +++ b/contrib/btree_gist/sql/bit.sql @@ -16,7 +16,6 @@ SELECT count(*) FROM bittmp WHERE a >= '011011000100010111011000110000100'; SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; -SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); DROP INDEX bitidx_b; diff --git a/contrib/btree_gist/sql/bytea.sql b/contrib/btree_gist/sql/bytea.sql index fdfa0c345b..184de9a04a 100644 --- a/contrib/btree_gist/sql/bytea.sql +++ b/contrib/btree_gist/sql/bytea.sql @@ -17,7 +17,6 @@ SELECT count(*) FROM byteatmp WHERE a >= '31b0'; SELECT count(*) FROM byteatmp WHERE a > '31b0'; -SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); DROP INDEX byteaidx_b; diff --git a/contrib/btree_gist/sql/cash.sql b/contrib/btree_gist/sql/cash.sql index 0581b3593e..be158b4ca9 100644 --- a/contrib/btree_gist/sql/cash.sql +++ b/contrib/btree_gist/sql/cash.sql @@ -18,7 +18,6 @@ SELECT count(*) FROM moneytmp WHERE a > '22649.64'; SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); DROP INDEX moneyidx_b; diff --git a/contrib/btree_gist/sql/char.sql b/contrib/btree_gist/sql/char.sql index 234eabee3b..7db4fd9f6f 100644 --- a/contrib/btree_gist/sql/char.sql +++ b/contrib/btree_gist/sql/char.sql @@ -16,7 +16,6 @@ SELECT count(*) FROM chartmp WHERE a >= '31b0'::char(32); SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); -SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); DROP INDEX charidx_b; diff --git a/contrib/btree_gist/sql/cidr.sql b/contrib/btree_gist/sql/cidr.sql index be2d22b079..a30490f674 100644 --- a/contrib/btree_gist/sql/cidr.sql +++ b/contrib/btree_gist/sql/cidr.sql @@ -15,7 +15,6 @@ SELECT count(*) FROM cidrtmp WHERE a >= '121.111.63.82'; SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; -SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); DROP INDEX cidridx_b; diff --git a/contrib/btree_gist/sql/date.sql b/contrib/btree_gist/sql/date.sql index f007402bac..d1c6292e2f 100644 --- a/contrib/btree_gist/sql/date.sql +++ b/contrib/btree_gist/sql/date.sql @@ -18,7 +18,6 @@ SELECT count(*) FROM datetmp WHERE a > '2001-02-13'; SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); DROP INDEX dateidx_b; diff --git a/contrib/btree_gist/sql/enum.sql b/contrib/btree_gist/sql/enum.sql index d6dbcb4239..4d169cf474 100644 --- a/contrib/btree_gist/sql/enum.sql +++ b/contrib/btree_gist/sql/enum.sql @@ -20,7 +20,6 @@ SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; -SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); DROP INDEX enumidx_b; diff --git a/contrib/btree_gist/sql/float4.sql b/contrib/btree_gist/sql/float4.sql index 0e3eb49343..8ccc8187e3 100644 --- a/contrib/btree_gist/sql/float4.sql +++ b/contrib/btree_gist/sql/float4.sql @@ -18,7 +18,6 @@ SELECT count(*) FROM float4tmp WHERE a > -179.0; SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); DROP INDEX float4idx_b; diff --git a/contrib/btree_gist/sql/float8.sql b/contrib/btree_gist/sql/float8.sql index 6a216dd606..1560bed6f9 100644 --- a/contrib/btree_gist/sql/float8.sql +++ b/contrib/btree_gist/sql/float8.sql @@ -18,7 +18,6 @@ SELECT count(*) FROM float8tmp WHERE a > -1890.0; SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); DROP INDEX float8idx_b; diff --git a/contrib/btree_gist/sql/inet.sql b/contrib/btree_gist/sql/inet.sql index 0339c853d3..60c4894116 100644 --- a/contrib/btree_gist/sql/inet.sql +++ b/contrib/btree_gist/sql/inet.sql @@ -16,7 +16,6 @@ SELECT count(*) FROM inettmp WHERE a >= '89.225.196.191'; SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; -SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); DROP INDEX inetidx_b; diff --git a/contrib/btree_gist/sql/int2.sql b/contrib/btree_gist/sql/int2.sql index bf98ac65f8..9781447bbc 100644 --- a/contrib/btree_gist/sql/int2.sql +++ b/contrib/btree_gist/sql/int2.sql @@ -18,7 +18,6 @@ SELECT count(*) FROM int2tmp WHERE a > 237; SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); DROP INDEX int2idx_b; diff --git a/contrib/btree_gist/sql/int4.sql b/contrib/btree_gist/sql/int4.sql index 214993314a..00d5d3a7c6 100644 --- a/contrib/btree_gist/sql/int4.sql +++ b/contrib/btree_gist/sql/int4.sql @@ -18,7 +18,6 @@ SELECT count(*) FROM int4tmp WHERE a > 237; SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); DROP INDEX int4idx_b; diff --git a/contrib/btree_gist/sql/int8.sql b/contrib/btree_gist/sql/int8.sql index 8a6c2a4bfd..20b93cab9e 100644 --- a/contrib/btree_gist/sql/int8.sql +++ b/contrib/btree_gist/sql/int8.sql @@ -18,7 +18,6 @@ SELECT count(*) FROM int8tmp WHERE a > 464571291354841; SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); DROP INDEX int8idx_b; diff --git a/contrib/btree_gist/sql/interval.sql b/contrib/btree_gist/sql/interval.sql index 6f9b1d4a39..07835eb0e8 100644 --- a/contrib/btree_gist/sql/interval.sql +++ b/contrib/btree_gist/sql/interval.sql @@ -18,7 +18,6 @@ SELECT count(*) FROM intervaltmp WHERE a > '199 days 21:21:23'; SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); DROP INDEX intervalidx_b; diff --git a/contrib/btree_gist/sql/macaddr.sql b/contrib/btree_gist/sql/macaddr.sql index bccfc820ca..c706b0cc19 100644 --- a/contrib/btree_gist/sql/macaddr.sql +++ b/contrib/btree_gist/sql/macaddr.sql @@ -16,7 +16,6 @@ SELECT count(*) FROM macaddrtmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; -SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); DROP INDEX macaddridx_b; diff --git a/contrib/btree_gist/sql/macaddr8.sql b/contrib/btree_gist/sql/macaddr8.sql index 2d0447a777..20bce939d5 100644 --- a/contrib/btree_gist/sql/macaddr8.sql +++ b/contrib/btree_gist/sql/macaddr8.sql @@ -16,7 +16,6 @@ SELECT count(*) FROM macaddr8tmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; -SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); DROP INDEX macaddr8idx_b; diff --git a/contrib/btree_gist/sql/numeric.sql b/contrib/btree_gist/sql/numeric.sql index 55ecbcdadc..553dae4dd1 100644 --- a/contrib/btree_gist/sql/numeric.sql +++ b/contrib/btree_gist/sql/numeric.sql @@ -40,7 +40,6 @@ SELECT count(*) FROM numerictmp WHERE a >= 0 ; SELECT count(*) FROM numerictmp WHERE a > 0 ; -SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); DROP INDEX numericidx_b; diff --git a/contrib/btree_gist/sql/oid.sql b/contrib/btree_gist/sql/oid.sql index bc9ee0cba3..e15493aa49 100644 --- a/contrib/btree_gist/sql/oid.sql +++ b/contrib/btree_gist/sql/oid.sql @@ -15,7 +15,6 @@ SELECT count(*) FROM oidtmp WHERE oid >= 17; SELECT count(*) FROM oidtmp WHERE oid > 17; -SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); DROP INDEX oididx_b; diff --git a/contrib/btree_gist/sql/text.sql b/contrib/btree_gist/sql/text.sql index 52705a216d..c44ea013e4 100644 --- a/contrib/btree_gist/sql/text.sql +++ b/contrib/btree_gist/sql/text.sql @@ -17,7 +17,6 @@ SELECT count(*) FROM texttmp WHERE a >= '31b0'; SELECT count(*) FROM texttmp WHERE a > '31b0'; -SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); DROP INDEX textidx_b; diff --git a/contrib/btree_gist/sql/time.sql b/contrib/btree_gist/sql/time.sql index 6123945213..eefca3901e 100644 --- a/contrib/btree_gist/sql/time.sql +++ b/contrib/btree_gist/sql/time.sql @@ -18,7 +18,6 @@ SELECT count(*) FROM timetmp WHERE a > '10:57:11'; SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); DROP INDEX timeidx_b; diff --git a/contrib/btree_gist/sql/timestamp.sql b/contrib/btree_gist/sql/timestamp.sql index 66a14f5ae5..b7c51c1ea3 100644 --- a/contrib/btree_gist/sql/timestamp.sql +++ b/contrib/btree_gist/sql/timestamp.sql @@ -18,7 +18,6 @@ SELECT count(*) FROM timestamptmp WHERE a > '2004-10-26 08:55:08'; SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); DROP INDEX timestampidx_b; diff --git a/contrib/btree_gist/sql/timestamptz.sql b/contrib/btree_gist/sql/timestamptz.sql index 2a92f63fc4..c21c849fe2 100644 --- a/contrib/btree_gist/sql/timestamptz.sql +++ b/contrib/btree_gist/sql/timestamptz.sql @@ -39,7 +39,6 @@ SELECT count(*) FROM timestamptztmp WHERE a > '2018-12-18 10:59:54 GMT+4'; SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); DROP INDEX timestamptzidx_b; diff --git a/contrib/btree_gist/sql/timetz.sql b/contrib/btree_gist/sql/timetz.sql index bc79d134b8..da11157360 100644 --- a/contrib/btree_gist/sql/timetz.sql +++ b/contrib/btree_gist/sql/timetz.sql @@ -41,8 +41,6 @@ INSERT INTO timetzcmp (r_id,a) SELECT 24,count(*) FROM timetztmp WHERE a >= '07: INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07:46:45 GMT+4'; - -SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); DROP INDEX timetzidx_b; diff --git a/contrib/btree_gist/sql/uuid.sql b/contrib/btree_gist/sql/uuid.sql index 7771bc0d82..121c33b171 100644 --- a/contrib/btree_gist/sql/uuid.sql +++ b/contrib/btree_gist/sql/uuid.sql @@ -16,7 +16,6 @@ SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; -SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); DROP INDEX uuididx_b; diff --git a/contrib/btree_gist/sql/varbit.sql b/contrib/btree_gist/sql/varbit.sql index 6d8243572b..b2159d3d0a 100644 --- a/contrib/btree_gist/sql/varbit.sql +++ b/contrib/btree_gist/sql/varbit.sql @@ -16,7 +16,6 @@ SELECT count(*) FROM varbittmp WHERE a >= '1110100111010'; SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; -SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); DROP INDEX varbitidx_b; diff --git a/contrib/btree_gist/sql/varchar.sql b/contrib/btree_gist/sql/varchar.sql index 59b77e0983..908654e367 100644 --- a/contrib/btree_gist/sql/varchar.sql +++ b/contrib/btree_gist/sql/varchar.sql @@ -16,7 +16,6 @@ SELECT count(*) FROM vchartmp WHERE a >= '31b0'::varchar(32); SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); -SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); DROP INDEX vcharidx_b; -- 2.24.3 (Apple Git-128) =
v7-0003-Fix-varlen-keys-sorting.patch (application/octet-stream, 3.3K) download From 424058ee8d1ab2641bdaac6d0f2ce67d818d4430 Mon Sep 17 00:00:00 2001 From: Andrey Borodin <amborodin@acm.org> Date: Fri, 7 May 2021 17:47:23 +0500 Subject: [PATCH v7 3/3] Fix varlen keys sorting --- contrib/btree_gist/btree_bit.c | 6 ++++-- contrib/btree_gist/btree_bytea.c | 6 ++++-- contrib/btree_gist/btree_numeric.c | 6 ++++-- contrib/btree_gist/btree_text.c | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c index 61b2eecfd5..947e3b63ac 100644 --- a/contrib/btree_gist/btree_bit.c +++ b/contrib/btree_gist/btree_bit.c @@ -214,10 +214,12 @@ gbt_bit_penalty(PG_FUNCTION_ARGS) static int gbt_bit_sort_build_cmp(Datum a, Datum b, SortSupport ssup) { + GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a)); + GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b)); /* Use byteacmp(), like gbt_bitcmp() does */ return DatumGetInt32(DirectFunctionCall2(byteacmp, - PointerGetDatum(a), - PointerGetDatum(b))); + PointerGetDatum(ra.lower), + PointerGetDatum(rb.lower))); } /* diff --git a/contrib/btree_gist/btree_bytea.c b/contrib/btree_gist/btree_bytea.c index a2abfb7d7c..9ffb3b9a5d 100644 --- a/contrib/btree_gist/btree_bytea.c +++ b/contrib/btree_gist/btree_bytea.c @@ -173,9 +173,11 @@ gbt_bytea_penalty(PG_FUNCTION_ARGS) static int gbt_bytea_sort_build_cmp(Datum a, Datum b, SortSupport ssup) { + GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a)); + GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b)); return DatumGetInt32(DirectFunctionCall2(byteacmp, - PointerGetDatum(a), - PointerGetDatum(b))); + PointerGetDatum(ra.lower), + PointerGetDatum(rb.lower))); } /* diff --git a/contrib/btree_gist/btree_numeric.c b/contrib/btree_gist/btree_numeric.c index face4e2b3a..76cd5d7380 100644 --- a/contrib/btree_gist/btree_numeric.c +++ b/contrib/btree_gist/btree_numeric.c @@ -232,9 +232,11 @@ gbt_numeric_picksplit(PG_FUNCTION_ARGS) static int gbt_numeric_sort_build_cmp(Datum a, Datum b, SortSupport ssup) { + GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a)); + GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b)); return DatumGetInt32(DirectFunctionCall2(numeric_cmp, - PointerGetDatum(a), - PointerGetDatum(b))); + PointerGetDatum(ra.lower), + PointerGetDatum(rb.lower))); } /* diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 01b1bda2f6..2deb8dd76f 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -244,10 +244,12 @@ gbt_text_penalty(PG_FUNCTION_ARGS) static int gbt_text_sort_build_cmp(Datum a, Datum b, SortSupport ssup) { + GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a)); + GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b)); return DatumGetInt32(DirectFunctionCall2Coll(bttextcmp, ssup->ssup_collation, - PointerGetDatum(a), - PointerGetDatum(b))); + PointerGetDatum(ra.lower), + PointerGetDatum(rb.lower))); } /* -- 2.24.3 (Apple Git-128) =
^ permalink raw reply [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-07-05 06:27  Emre Hasegeli <emre@hasegeli.com>
  parent: Andrey Borodin <x4mmm@yandex-team.ru>
  0 siblings, 2 replies; 111+ messages in thread

From: Emre Hasegeli @ 2021-07-05 06:27 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>

I tried reviewing the remaining patches.  It seems to work correctly,
and passes the tests on my laptop.

> In this pattern I flipped PointerGetDatum(a) to PointerGetDatum(ra.lower), because it seems to me correct. I've followed rule of thumb: every sort function must extract and use "lower" somehow. Though I suspect numeric a bit. Is it regular varlena?

As far as I understand, we cannot use the sortsupport functions from
the btree operator classes because the btree_gist extension handles
things differently.  This is unfortunate and a source of bugs [1], but
we cannot do anything about it.

Given that the lower and upper datums must be the same for the leaf
nodes, it makes sense to me to compare one of them.

Using numeric_cmp() for numeric in line with using bttextcmp() for text.

> +   /*
> +    * Numeric has abbreviation routines in numeric.c, but we don't try to use
> +    * them here. Maybe later.
> +    */

This is also true for text.  Perhaps we should also add a comment there.

> PFA patchset with v6 intact + two fixes of discovered issues.

> +   /* Use byteacmp(), like gbt_bitcmp() does */

We can improve this comment by incorporating Heikki's previous email:

> Ok, I think I understand that now. In btree_gist, the *_cmp() function
> operates on non-leaf values, and *_lt(), *_gt() et al operate on leaf
> values. For all other datatypes, the leaf and non-leaf representation is
> the same, but for bit/varbit, the non-leaf representation is different.
> The leaf representation is VarBit, and non-leaf is just the bits without
> the 'bit_len' field. That's why it is indeed correct for gbt_bitcmp() to
> just use byteacmp(), whereas gbt_bitlt() et al compares the 'bit_len'
> field separately. That's subtle, and 100% uncommented.

I think patch number 3 should be squashed to patch number 1.

I couldn't understand patch number 2 "Remove DEBUG1 verification".  It
seems like something rather useful.

[1] https://www.postgresql.org/message-id/flat/201010112055.o9BKtZf7011251%40wwwmaster.postgresql.org





^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-11-17 13:33  Daniel Gustafsson <daniel@yesql.se>
  parent: Emre Hasegeli <emre@hasegeli.com>
  1 sibling, 1 reply; 111+ messages in thread

From: Daniel Gustafsson @ 2021-11-17 13:33 UTC (permalink / raw)
  To: Andrey Borodin <x4mmm@yandex-team.ru>; +Cc: emre@hasegeli.com, Heikki Linnakangas <hlinnaka@iki.fi>; Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>

> On 5 Jul 2021, at 08:27, Emre Hasegeli <emre@hasegeli.com> wrote:

> ...
> 
> I couldn't understand patch number 2 "Remove DEBUG1 verification".  It
> seems like something rather useful.

These questions have gone unanswered since July, and the patch fails to apply
anymore.  Is there an updated version on the way?

--
Daniel Gustafsson		https://vmware.com/






^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-11-25 13:38  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Daniel Gustafsson <daniel@yesql.se>
  0 siblings, 0 replies; 111+ messages in thread

From: Andrey Borodin @ 2021-11-25 13:38 UTC (permalink / raw)
  To: Daniel Gustafsson <daniel@yesql.se>; +Cc: emre@hasegeli.com, Heikki Linnakangas <hlinnaka@iki.fi>; Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>



> 17 нояб. 2021 г., в 16:33, Daniel Gustafsson <daniel@yesql.se> написал(а):
> 
>> On 5 Jul 2021, at 08:27, Emre Hasegeli <emre@hasegeli.com> wrote:
> 
>> ...
>> 
>> I couldn't understand patch number 2 "Remove DEBUG1 verification".  It
>> seems like something rather useful.

Emre, thanks for the review! And sorry for this delay. Properly answering questions is still in my queue.

> 
> These questions have gone unanswered since July, and the patch fails to apply
> anymore.  Is there an updated version on the way?

Yes. In future versions I also want to address IOS vs pinned buffers issue[0]. And, probably, sort items on leaf pages. And, maybe, split pages more intelligently.
I hope to get to this in December.

I'll post rebased version ASAP.

Best regards, Andrey Borodin.

[0] https://www.postgresql.org/message-id/CAH2-Wz=PqOziyRSrnN5jAtfXWXY7-BJcHz9S355LH8Dt=5qxWQ@mail.gmail...




^ permalink  raw  reply  [nested|flat] 111+ messages in thread

* Re: Yet another fast GiST build
@ 2021-12-16 09:18  Andrey Borodin <x4mmm@yandex-team.ru>
  parent: Emre Hasegeli <emre@hasegeli.com>
  1 sibling, 0 replies; 111+ messages in thread

From: Andrey Borodin @ 2021-12-16 09:18 UTC (permalink / raw)
  To: emre@hasegeli.com; +Cc: Heikki Linnakangas <hlinnaka@iki.fi>; Peter Geoghegan <pg@bowt.ie>; Tom Lane <tgl@sss.pgh.pa.us>; Justin Pryzby <pryzby@telsasoft.com>; Darafei Komяpa Praliaskouski <me@komzpa.net>; Pavel Borisov <pashkin.elfe@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; pgsql-hackers; Ibrar Ahmed <ibrar.ahmad@gmail.com>

Hi Emre!

Thank you for the review.

> I tried reviewing the remaining patches. It seems to work correctly,
> and passes the tests on my laptop.
> 
>> In this pattern I flipped PointerGetDatum(a) to PointerGetDatum(ra.lower), because it seems to me correct. I've followed rule of thumb: every sort function must extract and use "lower" somehow. Though I suspect numeric a bit. Is it regular varlena?
> 
> As far as I understand, we cannot use the sortsupport functions from
> the btree operator classes because the btree_gist extension handles
> things differently. This is unfortunate and a source of bugs [1], but
> we cannot do anything about it.
> 
> Given that the lower and upper datums must be the same for the leaf
> nodes, it makes sense to me to compare one of them.
> 
> Using numeric_cmp() for numeric in line with using bttextcmp() for text.
OK.

>> + /*
>> + * Numeric has abbreviation routines in numeric.c, but we don't try to use
>> + * them here. Maybe later.
>> + */
> 
> This is also true for text. Perhaps we should also add a comment there.
Done.

> 
>> PFA patchset with v6 intact + two fixes of discovered issues.
> 
>> + /* Use byteacmp(), like gbt_bitcmp() does */
> 
> We can improve this comment by incorporating Heikki's previous email:
> 
>> Ok, I think I understand that now. In btree_gist, the *_cmp() function
>> operates on non-leaf values, and *_lt(), *_gt() et al operate on leaf
>> values. For all other datatypes, the leaf and non-leaf representation is
>> the same, but for bit/varbit, the non-leaf representation is different.
>> The leaf representation is VarBit, and non-leaf is just the bits without
>> the 'bit_len' field. That's why it is indeed correct for gbt_bitcmp() to
>> just use byteacmp(), whereas gbt_bitlt() et al compares the 'bit_len'
>> field separately. That's subtle, and 100% uncommented.
> 
Done.

> I think patch number 3 should be squashed to patch number 1.
All patches in the patchset expected to be squashed into 1 during commit.

> 
> I couldn't understand patch number 2 "Remove DEBUG1 verification". It
> seems like something rather useful.
If failed on buildfarm on some nodes. There were somewhat extroneous error messages.
Currently Step 1 and 2 are separete to ensure that opclasses are used correctly.

Thanks!

Best regards, Andrey Borodin.

Attachments: download all (4 files, .tar.gz)

v4-0001-Add-sortsupport-for-gist_btree-opclasses-for-fast.patch (application/octet-stream, 86.8K) download From 84637c43aad5a2168ff3e78710ddb43714a58679 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas <heikki.linnakangas@iki.fi> Date: Wed, 7 Apr 2021 13:22:05 +0300 Subject: [PATCH v4 1/4] Add sortsupport for gist_btree opclasses, for faster index builds. Commit 16fa9b2b30 introduced a faster way to build GiST indexes, by sorting all the data. This commit adds the sortsupport functions needed to make use of that feature for btree_gist. Author: Andrey Borodin Reviewer: Emre Hasegeli Discussion: https://www.postgresql.org/message-id/2F3F7265-0D22-44DB-AD71-8554C743D943@yandex-team.ru --- contrib/btree_gist/Makefile | 2 +- contrib/btree_gist/btree_bit.c | 25 +++ contrib/btree_gist/btree_bool.c | 71 ++++++++ contrib/btree_gist/btree_bytea.c | 26 ++- contrib/btree_gist/btree_cash.c | 80 +++++++++ contrib/btree_gist/btree_date.c | 27 +++ contrib/btree_gist/btree_enum.c | 70 ++++++++ contrib/btree_gist/btree_float4.c | 71 ++++++++ contrib/btree_gist/btree_float8.c | 77 ++++++++ contrib/btree_gist/btree_gist--1.7--1.8.sql | 189 ++++++++++++++++++++ contrib/btree_gist/btree_gist.control | 2 +- contrib/btree_gist/btree_gist.h | 1 + contrib/btree_gist/btree_inet.c | 77 ++++++++ contrib/btree_gist/btree_int2.c | 70 ++++++++ contrib/btree_gist/btree_int4.c | 70 ++++++++ contrib/btree_gist/btree_int8.c | 80 +++++++++ contrib/btree_gist/btree_interval.c | 27 +++ contrib/btree_gist/btree_macaddr.c | 78 ++++++++ contrib/btree_gist/btree_macaddr8.c | 78 ++++++++ contrib/btree_gist/btree_numeric.c | 29 +++ contrib/btree_gist/btree_oid.c | 70 ++++++++ contrib/btree_gist/btree_text.c | 25 +++ contrib/btree_gist/btree_time.c | 27 +++ contrib/btree_gist/btree_ts.c | 27 +++ contrib/btree_gist/btree_uuid.c | 25 +++ contrib/btree_gist/expected/bit.out | 7 + contrib/btree_gist/expected/bool.out | 7 + contrib/btree_gist/expected/bytea.out | 7 + contrib/btree_gist/expected/cash.out | 7 + contrib/btree_gist/expected/char.out | 7 + contrib/btree_gist/expected/cidr.out | 7 + contrib/btree_gist/expected/date.out | 7 + contrib/btree_gist/expected/enum.out | 7 + contrib/btree_gist/expected/float4.out | 7 + contrib/btree_gist/expected/float8.out | 7 + contrib/btree_gist/expected/inet.out | 7 + contrib/btree_gist/expected/int2.out | 7 + contrib/btree_gist/expected/int4.out | 7 + contrib/btree_gist/expected/int8.out | 7 + contrib/btree_gist/expected/interval.out | 7 + contrib/btree_gist/expected/macaddr.out | 7 + contrib/btree_gist/expected/macaddr8.out | 7 + contrib/btree_gist/expected/numeric.out | 7 + contrib/btree_gist/expected/oid.out | 7 + contrib/btree_gist/expected/text.out | 7 + contrib/btree_gist/expected/time.out | 7 + contrib/btree_gist/expected/timestamp.out | 7 + contrib/btree_gist/expected/timestamptz.out | 7 + contrib/btree_gist/expected/timetz.out | 7 + contrib/btree_gist/expected/uuid.out | 7 + contrib/btree_gist/expected/varbit.out | 7 + contrib/btree_gist/expected/varchar.out | 7 + contrib/btree_gist/sql/bit.sql | 4 + contrib/btree_gist/sql/bool.sql | 4 + contrib/btree_gist/sql/bytea.sql | 4 + contrib/btree_gist/sql/cash.sql | 4 + contrib/btree_gist/sql/char.sql | 4 + contrib/btree_gist/sql/cidr.sql | 4 + contrib/btree_gist/sql/date.sql | 4 + contrib/btree_gist/sql/enum.sql | 4 + contrib/btree_gist/sql/float4.sql | 4 + contrib/btree_gist/sql/float8.sql | 4 + contrib/btree_gist/sql/inet.sql | 4 + contrib/btree_gist/sql/int2.sql | 4 + contrib/btree_gist/sql/int4.sql | 4 + contrib/btree_gist/sql/int8.sql | 4 + contrib/btree_gist/sql/interval.sql | 4 + contrib/btree_gist/sql/macaddr.sql | 4 + contrib/btree_gist/sql/macaddr8.sql | 4 + contrib/btree_gist/sql/numeric.sql | 4 + contrib/btree_gist/sql/oid.sql | 4 + contrib/btree_gist/sql/text.sql | 4 + contrib/btree_gist/sql/time.sql | 4 + contrib/btree_gist/sql/timestamp.sql | 4 + contrib/btree_gist/sql/timestamptz.sql | 4 + contrib/btree_gist/sql/timetz.sql | 4 + contrib/btree_gist/sql/uuid.sql | 4 + contrib/btree_gist/sql/varbit.sql | 4 + contrib/btree_gist/sql/varchar.sql | 4 + src/backend/access/gist/gistbuild.c | 1 + 80 files changed, 1619 insertions(+), 3 deletions(-) create mode 100644 contrib/btree_gist/btree_gist--1.7--1.8.sql diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile index 48997c75f63..2bc7c9d71d5 100644 --- a/contrib/btree_gist/Makefile +++ b/contrib/btree_gist/Makefile @@ -33,7 +33,7 @@ EXTENSION = btree_gist DATA = btree_gist--1.0--1.1.sql \ btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \ - btree_gist--1.5--1.6.sql btree_gist--1.6--1.7.sql + btree_gist--1.5--1.6.sql btree_gist--1.6--1.7.sql btree_gist--1.7--1.8.sql PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes" REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \ diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c index 2225244ded5..61b2eecfd59 100644 --- a/contrib/btree_gist/btree_bit.c +++ b/contrib/btree_gist/btree_bit.c @@ -19,6 +19,7 @@ PG_FUNCTION_INFO_V1(gbt_bit_picksplit); PG_FUNCTION_INFO_V1(gbt_bit_consistent); PG_FUNCTION_INFO_V1(gbt_bit_penalty); PG_FUNCTION_INFO_V1(gbt_bit_same); +PG_FUNCTION_INFO_V1(gbt_bit_sortsupport); /* define for comparison */ @@ -209,3 +210,27 @@ gbt_bit_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bit_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + /* Use byteacmp(), like gbt_bitcmp() does */ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bit_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_bit_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_bool.c b/contrib/btree_gist/btree_bool.c index 1be246ea5e1..2f6997bf3de 100644 --- a/contrib/btree_gist/btree_bool.c +++ b/contrib/btree_gist/btree_bool.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_bool_picksplit); PG_FUNCTION_INFO_V1(gbt_bool_consistent); PG_FUNCTION_INFO_V1(gbt_bool_penalty); PG_FUNCTION_INFO_V1(gbt_bool_same); +PG_FUNCTION_INFO_V1(gbt_bool_sortsupport); static bool gbt_boolgt(const void *a, const void *b, FmgrInfo *flinfo) @@ -167,3 +168,73 @@ gbt_bool_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + + +static int +gbt_bool_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + boolKEY *ia = (boolKEY *) DatumGetPointer(a); + boolKEY *ib = (boolKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_bool_abbrev_convert(Datum original, SortSupport ssup) +{ + boolKEY *b1 = (boolKEY *) DatumGetPointer(original); + + return BoolGetDatum(b1->lower); +} + +static int +gbt_bool_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + bool a = DatumGetBool(z1); + bool b = DatumGetBool(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_bool_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bool_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_bool_cmp_abbrev; + ssup->abbrev_converter = gbt_bool_abbrev_convert; + ssup->abbrev_abort = gbt_bool_abbrev_abort; + ssup->abbrev_full_comparator = gbt_bool_sort_build_cmp; + } + else + { + ssup->comparator = gbt_bool_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_bytea.c b/contrib/btree_gist/btree_bytea.c index 6b005f0157e..a2abfb7d7c2 100644 --- a/contrib/btree_gist/btree_bytea.c +++ b/contrib/btree_gist/btree_bytea.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_bytea_picksplit); PG_FUNCTION_INFO_V1(gbt_bytea_consistent); PG_FUNCTION_INFO_V1(gbt_bytea_penalty); PG_FUNCTION_INFO_V1(gbt_bytea_same); +PG_FUNCTION_INFO_V1(gbt_bytea_sortsupport); /* define for comparison */ @@ -87,7 +88,7 @@ static const gbtree_vinfo tinfo = /************************************************** - * Text ops + * Bytea ops **************************************************/ @@ -168,3 +169,26 @@ gbt_bytea_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_bytea_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(byteacmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_bytea_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_bytea_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c index dfa23224b6f..dbd72d3ea08 100644 --- a/contrib/btree_gist/btree_cash.c +++ b/contrib/btree_gist/btree_cash.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_cash_consistent); PG_FUNCTION_INFO_V1(gbt_cash_distance); PG_FUNCTION_INFO_V1(gbt_cash_penalty); PG_FUNCTION_INFO_V1(gbt_cash_same); +PG_FUNCTION_INFO_V1(gbt_cash_sortsupport); static bool gbt_cashgt(const void *a, const void *b, FmgrInfo *flinfo) @@ -216,3 +217,82 @@ gbt_cash_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_cash_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + cashKEY *ia = (cashKEY *) DatumGetPointer(a); + cashKEY *ib = (cashKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_cash_abbrev_convert(Datum original, SortSupport ssup) +{ + cashKEY *b1 = (cashKEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return Int64GetDatum(z); +#else + return Int32GetDatum(z >> 32); +#endif +} + +static int +gbt_cash_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + int64 a = DatumGetInt64(z1); + int64 b = DatumGetInt64(z2); +#else + int32 a = DatumGetInt32(z1); + int32 b = DatumGetInt32(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_cash_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_cash_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_cash_cmp_abbrev; + ssup->abbrev_converter = gbt_cash_abbrev_convert; + ssup->abbrev_abort = gbt_cash_abbrev_abort; + ssup->abbrev_full_comparator = gbt_cash_sort_build_cmp; + } + else + { + ssup->comparator = gbt_cash_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c index 455a265a497..3abb6e9c475 100644 --- a/contrib/btree_gist/btree_date.c +++ b/contrib/btree_gist/btree_date.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_date_consistent); PG_FUNCTION_INFO_V1(gbt_date_distance); PG_FUNCTION_INFO_V1(gbt_date_penalty); PG_FUNCTION_INFO_V1(gbt_date_same); +PG_FUNCTION_INFO_V1(gbt_date_sortsupport); static bool gbt_dategt(const void *a, const void *b, FmgrInfo *flinfo) @@ -257,3 +258,29 @@ gbt_date_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_date_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + dateKEY *ia = (dateKEY *) PointerGetDatum(a); + dateKEY *ib = (dateKEY *) PointerGetDatum(b); + + return DatumGetInt32(DirectFunctionCall2(date_cmp, + DateADTGetDatum(ia->lower), + DateADTGetDatum(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_date_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_date_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_enum.c b/contrib/btree_gist/btree_enum.c index d4dc38a38e5..e8c5bc5ffe1 100644 --- a/contrib/btree_gist/btree_enum.c +++ b/contrib/btree_gist/btree_enum.c @@ -26,6 +26,7 @@ PG_FUNCTION_INFO_V1(gbt_enum_picksplit); PG_FUNCTION_INFO_V1(gbt_enum_consistent); PG_FUNCTION_INFO_V1(gbt_enum_penalty); PG_FUNCTION_INFO_V1(gbt_enum_same); +PG_FUNCTION_INFO_V1(gbt_enum_sortsupport); static bool @@ -183,3 +184,72 @@ gbt_enum_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_enum_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_enum_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + + return ObjectIdGetDatum(b1->lower); +} + +static int +gbt_enum_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + Oid a = DatumGetObjectId(z1); + Oid b = DatumGetObjectId(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_enum_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_enum_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_enum_cmp_abbrev; + ssup->abbrev_converter = gbt_enum_abbrev_convert; + ssup->abbrev_abort = gbt_enum_abbrev_abort; + ssup->abbrev_full_comparator = gbt_enum_sort_build_cmp; + } + else + { + ssup->comparator = gbt_enum_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c index b5d9175e6d5..3f212077cd4 100644 --- a/contrib/btree_gist/btree_float4.c +++ b/contrib/btree_gist/btree_float4.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_float4_consistent); PG_FUNCTION_INFO_V1(gbt_float4_distance); PG_FUNCTION_INFO_V1(gbt_float4_penalty); PG_FUNCTION_INFO_V1(gbt_float4_same); +PG_FUNCTION_INFO_V1(gbt_float4_sortsupport); static bool gbt_float4gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -211,3 +212,73 @@ gbt_float4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + + +static int +gbt_float4_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + float4KEY *ia = (float4KEY *) DatumGetPointer(a); + float4KEY *ib = (float4KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float4_abbrev_convert(Datum original, SortSupport ssup) +{ + float4KEY *b1 = (float4KEY *) DatumGetPointer(original); + + return Float4GetDatum(b1->lower); +} + +static int +gbt_float4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + float4 a = DatumGetFloat4(z1); + float4 b = DatumGetFloat4(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_float4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float4_cmp_abbrev; + ssup->abbrev_converter = gbt_float4_abbrev_convert; + ssup->abbrev_abort = gbt_float4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float4_sort_build_cmp; + } + else + { + ssup->comparator = gbt_float4_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c index 8fe1fedeef8..34212bd5400 100644 --- a/contrib/btree_gist/btree_float8.c +++ b/contrib/btree_gist/btree_float8.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_float8_consistent); PG_FUNCTION_INFO_V1(gbt_float8_distance); PG_FUNCTION_INFO_V1(gbt_float8_penalty); PG_FUNCTION_INFO_V1(gbt_float8_same); +PG_FUNCTION_INFO_V1(gbt_float8_sortsupport); static bool @@ -218,3 +219,79 @@ gbt_float8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_float8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + float8KEY *ia = (float8KEY *) DatumGetPointer(a); + float8KEY *ib = (float8KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_float8_abbrev_convert(Datum original, SortSupport ssup) +{ + float8KEY *b1 = (float8KEY *) DatumGetPointer(original); + float8 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return Float8GetDatum(z); +#else + return Float4GetDatum((float4) z); +#endif +} + +static int +gbt_float8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + float8 a = DatumGetFloat8(z1); + float8 b = DatumGetFloat8(z2); +#else + float4 a = DatumGetFloat4(z1); + float4 b = DatumGetFloat4(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +static bool +gbt_float8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_float8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_float8_cmp_abbrev; + ssup->abbrev_converter = gbt_float8_abbrev_convert; + ssup->abbrev_abort = gbt_float8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_float8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_float8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_gist--1.7--1.8.sql b/contrib/btree_gist/btree_gist--1.7--1.8.sql new file mode 100644 index 00000000000..639f1f2e731 --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.7--1.8.sql @@ -0,0 +1,189 @@ +/* contrib/btree_gist/btree_gist--1.7--1.8.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.8'" to load this file. \quit + + +CREATE FUNCTION gbt_int8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD + FUNCTION 11 (int8, int8) gbt_int8_sortsupport (internal) ; + +CREATE FUNCTION gbt_int4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD + FUNCTION 11 (int4, int4) gbt_int4_sortsupport (internal) ; + +CREATE FUNCTION gbt_int2_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD + FUNCTION 11 (int2, int2) gbt_int2_sortsupport (internal) ; + +CREATE FUNCTION gbt_float8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD + FUNCTION 11 (float8, float8) gbt_float8_sortsupport (internal) ; + +CREATE FUNCTION gbt_float4_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD + FUNCTION 11 (float4, float4) gbt_float4_sortsupport (internal) ; + +CREATE FUNCTION gbt_enum_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD + FUNCTION 11 (anyenum, anyenum) gbt_enum_sortsupport (internal) ; + +CREATE FUNCTION gbt_oid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD + FUNCTION 11 (oid, oid) gbt_oid_sortsupport (internal) ; + +CREATE FUNCTION gbt_cash_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD + FUNCTION 11 (money, money) gbt_cash_sortsupport (internal) ; + +CREATE FUNCTION gbt_inet_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD + FUNCTION 11 (inet, inet) gbt_inet_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD + FUNCTION 11 (cidr, cidr) gbt_inet_sortsupport (internal) ; + + +CREATE FUNCTION gbt_macad_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD + FUNCTION 11 (macaddr, macaddr) gbt_macad_sortsupport (internal) ; + +CREATE FUNCTION gbt_macad8_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD + FUNCTION 11 (macaddr8, macaddr8) gbt_macad8_sortsupport (internal) ; + +CREATE FUNCTION gbt_numeric_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD + FUNCTION 11 (numeric, numeric) gbt_numeric_sortsupport (internal) ; + +CREATE FUNCTION gbt_uuid_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD + FUNCTION 11 (uuid, uuid) gbt_uuid_sortsupport (internal) ; + +CREATE FUNCTION gbt_ts_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD + FUNCTION 11 (timestamp, timestamp) gbt_ts_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + FUNCTION 11 (timestamptz, timestamptz) gbt_ts_sortsupport (internal) ; + +CREATE FUNCTION gbt_text_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_text_ops USING gist ADD + FUNCTION 11 (text, text) gbt_text_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD + FUNCTION 11 (bpchar, bpchar) gbt_text_sortsupport (internal) ; + +CREATE FUNCTION gbt_time_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_time_ops USING gist ADD + FUNCTION 11 (time, time) gbt_time_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD + FUNCTION 11 (timetz, timetz) gbt_time_sortsupport (internal) ; + +CREATE FUNCTION gbt_bytea_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD + FUNCTION 11 (bytea, bytea) gbt_bytea_sortsupport (internal) ; + +CREATE FUNCTION gbt_date_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_date_ops USING gist ADD + FUNCTION 11 (date, date) gbt_date_sortsupport (internal) ; + +CREATE FUNCTION gbt_bit_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD + FUNCTION 11 (bit, bit) gbt_bit_sortsupport (internal) ; + +ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD + FUNCTION 11 (varbit, varbit) gbt_bit_sortsupport (internal) ; + +CREATE FUNCTION gbt_intv_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD + FUNCTION 11 (interval, interval) gbt_intv_sortsupport (internal) ; + +CREATE FUNCTION gbt_bool_sortsupport(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +ALTER OPERATOR FAMILY gist_bool_ops USING gist ADD + FUNCTION 11 (bool, bool) gbt_enum_sortsupport (internal) ; diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control index fa9171a80a2..abf66538f32 100644 --- a/contrib/btree_gist/btree_gist.control +++ b/contrib/btree_gist/btree_gist.control @@ -1,6 +1,6 @@ # btree_gist extension comment = 'support for indexing common datatypes in GiST' -default_version = '1.7' +default_version = '1.8' module_pathname = '$libdir/btree_gist' relocatable = true trusted = true diff --git a/contrib/btree_gist/btree_gist.h b/contrib/btree_gist/btree_gist.h index f22f14ac4ca..b2c680898d2 100644 --- a/contrib/btree_gist/btree_gist.h +++ b/contrib/btree_gist/btree_gist.h @@ -6,6 +6,7 @@ #include "access/nbtree.h" #include "fmgr.h" +#include "utils/sortsupport.h" #define BtreeGistNotEqualStrategyNumber 6 diff --git a/contrib/btree_gist/btree_inet.c b/contrib/btree_gist/btree_inet.c index e4b3a946b27..88136128cee 100644 --- a/contrib/btree_gist/btree_inet.c +++ b/contrib/btree_gist/btree_inet.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_inet_picksplit); PG_FUNCTION_INFO_V1(gbt_inet_consistent); PG_FUNCTION_INFO_V1(gbt_inet_penalty); PG_FUNCTION_INFO_V1(gbt_inet_same); +PG_FUNCTION_INFO_V1(gbt_inet_sortsupport); static bool @@ -186,3 +187,79 @@ gbt_inet_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_inet_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + inetKEY *ia = (inetKEY *) DatumGetPointer(a); + inetKEY *ib = (inetKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_inet_abbrev_convert(Datum original, SortSupport ssup) +{ + inetKEY *b1 = (inetKEY *) DatumGetPointer(original); + double z = b1->lower; + +#if SIZEOF_DATUM == 8 + return Float8GetDatum(z); +#else + return Float4GetDatum((float4) z); +#endif +} + +static int +gbt_inet_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + float8 a = DatumGetFloat8(z1); + float8 b = DatumGetFloat8(z2); +#else + float4 a = DatumGetFloat4(z1); + float4 b = DatumGetFloat4(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +static bool +gbt_inet_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_inet_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_inet_cmp_abbrev; + ssup->abbrev_converter = gbt_inet_abbrev_convert; + ssup->abbrev_abort = gbt_inet_abbrev_abort; + ssup->abbrev_full_comparator = gbt_inet_sort_build_cmp; + } + else + { + ssup->comparator = gbt_inet_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c index a91b95ff398..38ca3e05da7 100644 --- a/contrib/btree_gist/btree_int2.c +++ b/contrib/btree_gist/btree_int2.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int2_consistent); PG_FUNCTION_INFO_V1(gbt_int2_distance); PG_FUNCTION_INFO_V1(gbt_int2_penalty); PG_FUNCTION_INFO_V1(gbt_int2_same); +PG_FUNCTION_INFO_V1(gbt_int2_sortsupport); static bool gbt_int2gt(const void *a, const void *b, FmgrInfo *flinfo) @@ -214,3 +215,72 @@ gbt_int2_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int2_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int16KEY *ia = (int16KEY *) DatumGetPointer(a); + int16KEY *ib = (int16KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int2_abbrev_convert(Datum original, SortSupport ssup) +{ + int16KEY *b1 = (int16KEY *) DatumGetPointer(original); + + return Int16GetDatum(b1->lower); +} + +static int +gbt_int2_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + int16 a = DatumGetInt16(z1); + int16 b = DatumGetInt16(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int2_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int2_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int2_cmp_abbrev; + ssup->abbrev_converter = gbt_int2_abbrev_convert; + ssup->abbrev_abort = gbt_int2_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int2_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int2_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c index 7ea98c478c7..21bd01ed10d 100644 --- a/contrib/btree_gist/btree_int4.c +++ b/contrib/btree_gist/btree_int4.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int4_consistent); PG_FUNCTION_INFO_V1(gbt_int4_distance); PG_FUNCTION_INFO_V1(gbt_int4_penalty); PG_FUNCTION_INFO_V1(gbt_int4_same); +PG_FUNCTION_INFO_V1(gbt_int4_sortsupport); static bool @@ -215,3 +216,72 @@ gbt_int4_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int4_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int32KEY *ia = (int32KEY *) DatumGetPointer(a); + int32KEY *ib = (int32KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int4_abbrev_convert(Datum original, SortSupport ssup) +{ + int32KEY *b1 = (int32KEY *) DatumGetPointer(original); + + return Int32GetDatum(b1->lower); +} + +static int +gbt_int4_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + int32 a = DatumGetInt32(z1); + int32 b = DatumGetInt32(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int4_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int4_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int4_cmp_abbrev; + ssup->abbrev_converter = gbt_int4_abbrev_convert; + ssup->abbrev_abort = gbt_int4_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int4_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int4_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c index df2b0d174b9..b6e7fe68742 100644 --- a/contrib/btree_gist/btree_int8.c +++ b/contrib/btree_gist/btree_int8.c @@ -24,6 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_int8_consistent); PG_FUNCTION_INFO_V1(gbt_int8_distance); PG_FUNCTION_INFO_V1(gbt_int8_penalty); PG_FUNCTION_INFO_V1(gbt_int8_same); +PG_FUNCTION_INFO_V1(gbt_int8_sortsupport); static bool @@ -215,3 +216,82 @@ gbt_int8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_int8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + int64KEY *ia = (int64KEY *) DatumGetPointer(a); + int64KEY *ib = (int64KEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_int8_abbrev_convert(Datum original, SortSupport ssup) +{ + int64KEY *b1 = (int64KEY *) DatumGetPointer(original); + int64 z = b1->lower; + +#if SIZEOF_DATUM == 8 + return Int64GetDatum(z); +#else + return Int32GetDatum(z >> 32); +#endif +} + +static int +gbt_int8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + int64 a = DatumGetInt64(z1); + int64 b = DatumGetInt64(z2); +#else + int32 a = DatumGetInt32(z1); + int32 b = DatumGetInt32(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_int8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_int8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_int8_cmp_abbrev; + ssup->abbrev_converter = gbt_int8_abbrev_convert; + ssup->abbrev_abort = gbt_int8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_int8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_int8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c index a4b3b2b1e6f..0041acd3ddd 100644 --- a/contrib/btree_gist/btree_interval.c +++ b/contrib/btree_gist/btree_interval.c @@ -27,6 +27,7 @@ PG_FUNCTION_INFO_V1(gbt_intv_consistent); PG_FUNCTION_INFO_V1(gbt_intv_distance); PG_FUNCTION_INFO_V1(gbt_intv_penalty); PG_FUNCTION_INFO_V1(gbt_intv_same); +PG_FUNCTION_INFO_V1(gbt_intv_sortsupport); static bool @@ -297,3 +298,29 @@ gbt_intv_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_intv_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + intvKEY *ia = (intvKEY *) DatumGetPointer(a); + intvKEY *ib = (intvKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(interval_cmp, + IntervalPGetDatum(&ia->lower), + IntervalPGetDatum(&ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_intv_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_intv_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_macaddr.c b/contrib/btree_gist/btree_macaddr.c index 7f0e9e9c912..805148575d7 100644 --- a/contrib/btree_gist/btree_macaddr.c +++ b/contrib/btree_gist/btree_macaddr.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad_picksplit); PG_FUNCTION_INFO_V1(gbt_macad_consistent); PG_FUNCTION_INFO_V1(gbt_macad_penalty); PG_FUNCTION_INFO_V1(gbt_macad_same); +PG_FUNCTION_INFO_V1(gbt_macad_sortsupport); static bool @@ -195,3 +196,80 @@ gbt_macad_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + macKEY *ma = (macKEY *) DatumGetPointer(a); + macKEY *mb = (macKEY *) DatumGetPointer(b); + uint64 ia = mac_2_uint64(&ma->lower); + uint64 ib = mac_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + return 0; + + return (ia > ib) ? 1 : -1; +} + +static Datum +gbt_macad_abbrev_convert(Datum original, SortSupport ssup) +{ + macKEY *b1 = (macKEY *) DatumGetPointer(original); + uint64 z = mac_2_uint64(&b1->lower); + +#if SIZEOF_DATUM == 8 + return UInt64GetDatum(z); +#else + /* use the high 32 bits of the 48-bit integer */ + return UInt32GetDatum(z >> 16); +#endif +} + +static int +gbt_macad_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + uint64 a = DatumGetUInt64(z1); + uint64 b = DatumGetUInt64(z2); +#else + uint32 a = DatumGetUInt32(z1); + uint32 b = DatumGetUInt32(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +static bool +gbt_macad_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad_cmp_abbrev; + ssup->abbrev_converter = gbt_macad_abbrev_convert; + ssup->abbrev_abort = gbt_macad_abbrev_abort; + ssup->abbrev_full_comparator = gbt_macad_sort_build_cmp; + } + else + { + ssup->comparator = gbt_macad_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_macaddr8.c b/contrib/btree_gist/btree_macaddr8.c index ab4bca5d50d..a0514727e35 100644 --- a/contrib/btree_gist/btree_macaddr8.c +++ b/contrib/btree_gist/btree_macaddr8.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_macad8_picksplit); PG_FUNCTION_INFO_V1(gbt_macad8_consistent); PG_FUNCTION_INFO_V1(gbt_macad8_penalty); PG_FUNCTION_INFO_V1(gbt_macad8_same); +PG_FUNCTION_INFO_V1(gbt_macad8_sortsupport); static bool @@ -195,3 +196,80 @@ gbt_macad8_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_macad8_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + mac8KEY *ma = (mac8KEY *) DatumGetPointer(a); + mac8KEY *mb = (mac8KEY *) DatumGetPointer(b); + uint64 ia = mac8_2_uint64(&ma->lower); + uint64 ib = mac8_2_uint64(&mb->lower); + + /* for leaf items we expect lower == upper */ + + if (ia == ib) + return 0; + + return (ia > ib) ? 1 : -1; +} + +static Datum +gbt_macad8_abbrev_convert(Datum original, SortSupport ssup) +{ + mac8KEY *b1 = (mac8KEY *) DatumGetPointer(original); + uint64 z = mac8_2_uint64(&b1->lower); + +#if SIZEOF_DATUM == 8 + return UInt64GetDatum(z); +#else + /* use the high bits only */ + return UInt32GetDatum(z >> 32); +#endif +} + +static int +gbt_macad8_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ +#if SIZEOF_DATUM == 8 + uint64 a = DatumGetUInt64(z1); + uint64 b = DatumGetUInt64(z2); +#else + uint32 a = DatumGetUInt32(z1); + uint32 b = DatumGetUInt32(z2); +#endif + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +static bool +gbt_macad8_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_macad8_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_macad8_cmp_abbrev; + ssup->abbrev_converter = gbt_macad8_abbrev_convert; + ssup->abbrev_abort = gbt_macad8_abbrev_abort; + ssup->abbrev_full_comparator = gbt_macad8_sort_build_cmp; + } + else + { + ssup->comparator = gbt_macad8_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_numeric.c b/contrib/btree_gist/btree_numeric.c index 35e466cdd94..face4e2b3af 100644 --- a/contrib/btree_gist/btree_numeric.c +++ b/contrib/btree_gist/btree_numeric.c @@ -21,6 +21,7 @@ PG_FUNCTION_INFO_V1(gbt_numeric_picksplit); PG_FUNCTION_INFO_V1(gbt_numeric_consistent); PG_FUNCTION_INFO_V1(gbt_numeric_penalty); PG_FUNCTION_INFO_V1(gbt_numeric_same); +PG_FUNCTION_INFO_V1(gbt_numeric_sortsupport); /* define for comparison */ @@ -227,3 +228,31 @@ gbt_numeric_picksplit(PG_FUNCTION_ARGS) &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(v); } + +static int +gbt_numeric_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2(numeric_cmp, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_numeric_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_numeric_sort_build_cmp; + + /* + * Numeric has abbreviation routines in numeric.c, but we don't try to use + * them here. Maybe later. + */ + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c index 3cc7d4245d4..9b7c546aeeb 100644 --- a/contrib/btree_gist/btree_oid.c +++ b/contrib/btree_gist/btree_oid.c @@ -23,6 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_oid_consistent); PG_FUNCTION_INFO_V1(gbt_oid_distance); PG_FUNCTION_INFO_V1(gbt_oid_penalty); PG_FUNCTION_INFO_V1(gbt_oid_same); +PG_FUNCTION_INFO_V1(gbt_oid_sortsupport); static bool @@ -215,3 +216,72 @@ gbt_oid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_oid_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + oidKEY *ia = (oidKEY *) DatumGetPointer(a); + oidKEY *ib = (oidKEY *) DatumGetPointer(b); + + /* for leaf items we expect lower == upper */ + Assert(ia->lower == ia->upper); + Assert(ib->lower == ib->upper); + + if (ia->lower == ib->lower) + return 0; + + return (ia->lower > ib->lower) ? 1 : -1; +} + +static Datum +gbt_oid_abbrev_convert(Datum original, SortSupport ssup) +{ + oidKEY *b1 = (oidKEY *) DatumGetPointer(original); + + return ObjectIdGetDatum(b1->lower); +} + +static int +gbt_oid_cmp_abbrev(Datum z1, Datum z2, SortSupport ssup) +{ + Oid a = DatumGetObjectId(z1); + Oid b = DatumGetObjectId(z2); + + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +/* + * We never consider aborting the abbreviation. + */ +static bool +gbt_oid_abbrev_abort(int memtupcount, SortSupport ssup) +{ + return false; +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_oid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + if (ssup->abbreviate) + { + ssup->comparator = gbt_oid_cmp_abbrev; + ssup->abbrev_converter = gbt_oid_abbrev_convert; + ssup->abbrev_abort = gbt_oid_abbrev_abort; + ssup->abbrev_full_comparator = gbt_oid_sort_build_cmp; + } + else + { + ssup->comparator = gbt_oid_sort_build_cmp; + } + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 8019d112819..01b1bda2f66 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -18,6 +18,7 @@ PG_FUNCTION_INFO_V1(gbt_text_consistent); PG_FUNCTION_INFO_V1(gbt_bpchar_consistent); PG_FUNCTION_INFO_V1(gbt_text_penalty); PG_FUNCTION_INFO_V1(gbt_text_same); +PG_FUNCTION_INFO_V1(gbt_text_sortsupport); /* define for comparison */ @@ -239,3 +240,27 @@ gbt_text_penalty(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo)); } + +static int +gbt_text_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + return DatumGetInt32(DirectFunctionCall2Coll(bttextcmp, + ssup->ssup_collation, + PointerGetDatum(a), + PointerGetDatum(b))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_text_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_text_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c index fd8774a2f08..c021f675142 100644 --- a/contrib/btree_gist/btree_time.c +++ b/contrib/btree_gist/btree_time.c @@ -28,6 +28,7 @@ PG_FUNCTION_INFO_V1(gbt_time_distance); PG_FUNCTION_INFO_V1(gbt_timetz_consistent); PG_FUNCTION_INFO_V1(gbt_time_penalty); PG_FUNCTION_INFO_V1(gbt_time_same); +PG_FUNCTION_INFO_V1(gbt_time_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -332,3 +333,29 @@ gbt_time_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_time_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + timeKEY *ia = (timeKEY *) DatumGetPointer(a); + timeKEY *ib = (timeKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(time_cmp, + TimeADTGetDatumFast(ia->lower), + TimeADTGetDatumFast(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_time_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_time_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c index 2671ba961cd..c6ef0782d24 100644 --- a/contrib/btree_gist/btree_ts.c +++ b/contrib/btree_gist/btree_ts.c @@ -31,6 +31,7 @@ PG_FUNCTION_INFO_V1(gbt_tstz_consistent); PG_FUNCTION_INFO_V1(gbt_tstz_distance); PG_FUNCTION_INFO_V1(gbt_ts_penalty); PG_FUNCTION_INFO_V1(gbt_ts_same); +PG_FUNCTION_INFO_V1(gbt_ts_sortsupport); #ifdef USE_FLOAT8_BYVAL @@ -399,3 +400,29 @@ gbt_ts_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_ts_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + tsKEY *ia = (tsKEY *) DatumGetPointer(a); + tsKEY *ib = (tsKEY *) DatumGetPointer(b); + + return DatumGetInt32(DirectFunctionCall2(timestamp_cmp, + TimestampGetDatumFast(ia->lower), + TimestampGetDatumFast(ib->lower))); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_ts_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_ts_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/btree_uuid.c b/contrib/btree_gist/btree_uuid.c index b81875979a3..c802bf95a90 100644 --- a/contrib/btree_gist/btree_uuid.c +++ b/contrib/btree_gist/btree_uuid.c @@ -25,6 +25,7 @@ PG_FUNCTION_INFO_V1(gbt_uuid_picksplit); PG_FUNCTION_INFO_V1(gbt_uuid_consistent); PG_FUNCTION_INFO_V1(gbt_uuid_penalty); PG_FUNCTION_INFO_V1(gbt_uuid_same); +PG_FUNCTION_INFO_V1(gbt_uuid_sortsupport); static int @@ -233,3 +234,27 @@ gbt_uuid_same(PG_FUNCTION_ARGS) *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } + +static int +gbt_uuid_sort_build_cmp(Datum a, Datum b, SortSupport ssup) +{ + uuidKEY *ua = (uuidKEY *) DatumGetPointer(a); + uuidKEY *ub = (uuidKEY *) DatumGetPointer(b); + + return uuid_internal_cmp(&ua->lower, &ub->lower); +} + +/* + * Sort support routine for fast GiST index build by sorting. + */ +Datum +gbt_uuid_sortsupport(PG_FUNCTION_ARGS) +{ + SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + + ssup->comparator = gbt_uuid_sort_build_cmp; + ssup->abbrev_converter = NULL; + ssup->abbrev_abort = NULL; + ssup->abbrev_full_comparator = NULL; + PG_RETURN_VOID(); +} diff --git a/contrib/btree_gist/expected/bit.out b/contrib/btree_gist/expected/bit.out index e57871f310b..cb2297ce806 100644 --- a/contrib/btree_gist/expected/bit.out +++ b/contrib/btree_gist/expected/bit.out @@ -32,7 +32,14 @@ SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; 350 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); +DEBUG: building index "bitidx" on table "bittmp" serially +DEBUG: using sorted GiST build +CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "bitidx_b" on table "bittmp" serially +DROP INDEX bitidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM bittmp WHERE a < '011011000100010111011000110000100'; count diff --git a/contrib/btree_gist/expected/bool.out b/contrib/btree_gist/expected/bool.out index c67a9685d52..f015cbf0623 100644 --- a/contrib/btree_gist/expected/bool.out +++ b/contrib/btree_gist/expected/bool.out @@ -32,7 +32,14 @@ SELECT count(*) FROM booltmp WHERE a > true; 0 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX boolidx ON booltmp USING gist ( a ); +DEBUG: building index "boolidx" on table "booltmp" serially +DEBUG: using sorted GiST build +CREATE INDEX boolidx_b ON booltmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "boolidx_b" on table "booltmp" serially +DROP INDEX boolidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM booltmp WHERE a < true; count diff --git a/contrib/btree_gist/expected/bytea.out b/contrib/btree_gist/expected/bytea.out index b9efa73c085..170b48e1db9 100644 --- a/contrib/btree_gist/expected/bytea.out +++ b/contrib/btree_gist/expected/bytea.out @@ -33,7 +33,14 @@ SELECT count(*) FROM byteatmp WHERE a > '31b0'; 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); +DEBUG: building index "byteaidx" on table "byteatmp" serially +DEBUG: using sorted GiST build +CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "byteaidx_b" on table "byteatmp" serially +DROP INDEX byteaidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM byteatmp WHERE a < '31b0'::bytea; count diff --git a/contrib/btree_gist/expected/cash.out b/contrib/btree_gist/expected/cash.out index 7fbc7355929..868af70b22c 100644 --- a/contrib/btree_gist/expected/cash.out +++ b/contrib/btree_gist/expected/cash.out @@ -40,7 +40,14 @@ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; $21,915.01 | $442.22 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); +DEBUG: building index "moneyidx" on table "moneytmp" serially +DEBUG: using sorted GiST build +CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "moneyidx_b" on table "moneytmp" serially +DROP INDEX moneyidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM moneytmp WHERE a < '22649.64'::money; count diff --git a/contrib/btree_gist/expected/char.out b/contrib/btree_gist/expected/char.out index d715c045cc1..97316cbb06b 100644 --- a/contrib/btree_gist/expected/char.out +++ b/contrib/btree_gist/expected/char.out @@ -32,7 +32,14 @@ SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); +DEBUG: building index "charidx" on table "chartmp" serially +DEBUG: using sorted GiST build +CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "charidx_b" on table "chartmp" serially +DROP INDEX charidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM chartmp WHERE a < '31b0'::char(32); count diff --git a/contrib/btree_gist/expected/cidr.out b/contrib/btree_gist/expected/cidr.out index 6d0995add60..f15597c06a0 100644 --- a/contrib/btree_gist/expected/cidr.out +++ b/contrib/btree_gist/expected/cidr.out @@ -32,7 +32,14 @@ SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; 309 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); +DEBUG: building index "cidridx" on table "cidrtmp" serially +DEBUG: using sorted GiST build +CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "cidridx_b" on table "cidrtmp" serially +DROP INDEX cidridx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM cidrtmp WHERE a < '121.111.63.82'::cidr; count diff --git a/contrib/btree_gist/expected/date.out b/contrib/btree_gist/expected/date.out index 5db864bb82c..5c93d02209c 100644 --- a/contrib/btree_gist/expected/date.out +++ b/contrib/btree_gist/expected/date.out @@ -40,7 +40,14 @@ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; 03-24-2001 | 39 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); +DEBUG: building index "dateidx" on table "datetmp" serially +DEBUG: using sorted GiST build +CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "dateidx_b" on table "datetmp" serially +DROP INDEX dateidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM datetmp WHERE a < '2001-02-13'::date; count diff --git a/contrib/btree_gist/expected/enum.out b/contrib/btree_gist/expected/enum.out index c4b769dd4b7..d73ad33974d 100644 --- a/contrib/btree_gist/expected/enum.out +++ b/contrib/btree_gist/expected/enum.out @@ -46,7 +46,14 @@ SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; 230 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); +DEBUG: building index "enumidx" on table "enumtmp" serially +DEBUG: using sorted GiST build +CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "enumidx_b" on table "enumtmp" serially +DROP INDEX enumidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow; count diff --git a/contrib/btree_gist/expected/float4.out b/contrib/btree_gist/expected/float4.out index dfe732049e6..5f4f1aa4ec5 100644 --- a/contrib/btree_gist/expected/float4.out +++ b/contrib/btree_gist/expected/float4.out @@ -40,7 +40,14 @@ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; -158.17741 | 20.822586 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); +DEBUG: building index "float4idx" on table "float4tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "float4idx_b" on table "float4tmp" serially +DROP INDEX float4idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM float4tmp WHERE a < -179.0::float4; count diff --git a/contrib/btree_gist/expected/float8.out b/contrib/btree_gist/expected/float8.out index ebd0ef3d689..4db0f7b8282 100644 --- a/contrib/btree_gist/expected/float8.out +++ b/contrib/btree_gist/expected/float8.out @@ -40,7 +40,14 @@ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; -1769.73634 | 120.26366000000007 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); +DEBUG: building index "float8idx" on table "float8tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "float8idx_b" on table "float8tmp" serially +DROP INDEX float8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM float8tmp WHERE a < -1890.0::float8; count diff --git a/contrib/btree_gist/expected/inet.out b/contrib/btree_gist/expected/inet.out index c323d903da4..0847d3b7d12 100644 --- a/contrib/btree_gist/expected/inet.out +++ b/contrib/btree_gist/expected/inet.out @@ -32,7 +32,14 @@ SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; 386 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); +DEBUG: building index "inetidx" on table "inettmp" serially +DEBUG: using sorted GiST build +CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "inetidx_b" on table "inettmp" serially +DROP INDEX inetidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM inettmp WHERE a < '89.225.196.191'::inet; count diff --git a/contrib/btree_gist/expected/int2.out b/contrib/btree_gist/expected/int2.out index 50a332939bd..9ad06a8dce0 100644 --- a/contrib/btree_gist/expected/int2.out +++ b/contrib/btree_gist/expected/int2.out @@ -40,7 +40,14 @@ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); +DEBUG: building index "int2idx" on table "int2tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int2idx_b" on table "int2tmp" serially +DROP INDEX int2idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int2tmp WHERE a < 237::int2; count diff --git a/contrib/btree_gist/expected/int4.out b/contrib/btree_gist/expected/int4.out index 6bbdc7c3f4b..fdf143f32c3 100644 --- a/contrib/btree_gist/expected/int4.out +++ b/contrib/btree_gist/expected/int4.out @@ -40,7 +40,14 @@ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); +DEBUG: building index "int4idx" on table "int4tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int4idx_b" on table "int4tmp" serially +DROP INDEX int4idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int4tmp WHERE a < 237::int4; count diff --git a/contrib/btree_gist/expected/int8.out b/contrib/btree_gist/expected/int8.out index eff77c26b5a..532c4e5e704 100644 --- a/contrib/btree_gist/expected/int8.out +++ b/contrib/btree_gist/expected/int8.out @@ -40,7 +40,14 @@ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' 478227196042750 | 13655904687909 (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); +DEBUG: building index "int8idx" on table "int8tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "int8idx_b" on table "int8tmp" serially +DROP INDEX int8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int8tmp WHERE a < 464571291354841::int8; count diff --git a/contrib/btree_gist/expected/interval.out b/contrib/btree_gist/expected/interval.out index 4c3d494e4a6..12d50fdf581 100644 --- a/contrib/btree_gist/expected/interval.out +++ b/contrib/btree_gist/expected/interval.out @@ -40,7 +40,14 @@ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21 @ 220 days 19 hours 5 mins 42 secs | @ 21 days -2 hours -15 mins -41 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); +DEBUG: building index "intervalidx" on table "intervaltmp" serially +DEBUG: using sorted GiST build +CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "intervalidx_b" on table "intervaltmp" serially +DROP INDEX intervalidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM intervaltmp WHERE a < '199 days 21:21:23'::interval; count diff --git a/contrib/btree_gist/expected/macaddr.out b/contrib/btree_gist/expected/macaddr.out index c0a4c6287f3..9634000618f 100644 --- a/contrib/btree_gist/expected/macaddr.out +++ b/contrib/btree_gist/expected/macaddr.out @@ -32,7 +32,14 @@ SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); +DEBUG: building index "macaddridx" on table "macaddrtmp" serially +DEBUG: using sorted GiST build +CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "macaddridx_b" on table "macaddrtmp" serially +DROP INDEX macaddridx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM macaddrtmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr; count diff --git a/contrib/btree_gist/expected/macaddr8.out b/contrib/btree_gist/expected/macaddr8.out index e5ec6a5deab..910223cd3b2 100644 --- a/contrib/btree_gist/expected/macaddr8.out +++ b/contrib/btree_gist/expected/macaddr8.out @@ -32,7 +32,14 @@ SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); +DEBUG: building index "macaddr8idx" on table "macaddr8tmp" serially +DEBUG: using sorted GiST build +CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "macaddr8idx_b" on table "macaddr8tmp" serially +DROP INDEX macaddr8idx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM macaddr8tmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr8; count diff --git a/contrib/btree_gist/expected/numeric.out b/contrib/btree_gist/expected/numeric.out index ae839b8ec83..8dce480c303 100644 --- a/contrib/btree_gist/expected/numeric.out +++ b/contrib/btree_gist/expected/numeric.out @@ -94,7 +94,14 @@ SELECT count(*) FROM numerictmp WHERE a > 0 ; 576 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); +DEBUG: building index "numericidx" on table "numerictmp" serially +DEBUG: using sorted GiST build +CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "numericidx_b" on table "numerictmp" serially +DROP INDEX numericidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM numerictmp WHERE a < -1890.0; count diff --git a/contrib/btree_gist/expected/oid.out b/contrib/btree_gist/expected/oid.out index 776bbb10267..da27172609b 100644 --- a/contrib/btree_gist/expected/oid.out +++ b/contrib/btree_gist/expected/oid.out @@ -32,7 +32,14 @@ SELECT count(*) FROM oidtmp WHERE oid > 17; 983 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); +DEBUG: building index "oididx" on table "oidtmp" serially +DEBUG: using sorted GiST build +CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); +DEBUG: building index "oididx_b" on table "oidtmp" serially +DROP INDEX oididx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM oidtmp WHERE oid < 17; count diff --git a/contrib/btree_gist/expected/text.out b/contrib/btree_gist/expected/text.out index bb4e2e62d1d..2e760d14871 100644 --- a/contrib/btree_gist/expected/text.out +++ b/contrib/btree_gist/expected/text.out @@ -33,7 +33,14 @@ SELECT count(*) FROM texttmp WHERE a > '31b0'; 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); +DEBUG: building index "textidx" on table "texttmp" serially +DEBUG: using sorted GiST build +CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "textidx_b" on table "texttmp" serially +DROP INDEX textidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM texttmp WHERE a < '31b0'::text; count diff --git a/contrib/btree_gist/expected/time.out b/contrib/btree_gist/expected/time.out index ec95ef77c57..9b81e58ed4d 100644 --- a/contrib/btree_gist/expected/time.out +++ b/contrib/btree_gist/expected/time.out @@ -40,7 +40,14 @@ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; 10:55:32 | @ 1 min 39 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); +DEBUG: building index "timeidx" on table "timetmp" serially +DEBUG: using sorted GiST build +CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timeidx_b" on table "timetmp" serially +DROP INDEX timeidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timetmp WHERE a < '10:57:11'::time; count diff --git a/contrib/btree_gist/expected/timestamp.out b/contrib/btree_gist/expected/timestamp.out index 0d94f2f245c..8ea9897551c 100644 --- a/contrib/btree_gist/expected/timestamp.out +++ b/contrib/btree_gist/expected/timestamp.out @@ -40,7 +40,14 @@ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10- Mon Nov 29 20:12:43 2004 | @ 34 days 11 hours 17 mins 35 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); +DEBUG: building index "timestampidx" on table "timestamptmp" serially +DEBUG: using sorted GiST build +CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timestampidx_b" on table "timestamptmp" serially +DROP INDEX timestampidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timestamptmp WHERE a < '2004-10-26 08:55:08'::timestamp; count diff --git a/contrib/btree_gist/expected/timestamptz.out b/contrib/btree_gist/expected/timestamptz.out index 75a15a42568..2ba0dcd7ede 100644 --- a/contrib/btree_gist/expected/timestamptz.out +++ b/contrib/btree_gist/expected/timestamptz.out @@ -100,7 +100,14 @@ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> ' Thu Jan 24 12:28:12 2019 PST | @ 37 days 7 hours 28 mins 18 secs (3 rows) +SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); +DEBUG: building index "timestamptzidx" on table "timestamptztmp" serially +DEBUG: using sorted GiST build +CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timestamptzidx_b" on table "timestamptztmp" serially +DROP INDEX timestamptzidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timestamptztmp WHERE a < '2018-12-18 10:59:54 GMT+3'::timestamptz; count diff --git a/contrib/btree_gist/expected/timetz.out b/contrib/btree_gist/expected/timetz.out index 7f73e447977..6c855bfcd64 100644 --- a/contrib/btree_gist/expected/timetz.out +++ b/contrib/btree_gist/expected/timetz.out @@ -18,7 +18,14 @@ INSERT INTO timetzcmp (r_id,a) SELECT 22,count(*) FROM timetztmp WHERE a <= '07: INSERT INTO timetzcmp (r_id,a) SELECT 23,count(*) FROM timetztmp WHERE a = '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 24,count(*) FROM timetztmp WHERE a >= '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07:46:45 GMT+4'; +SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); +DEBUG: building index "timetzidx" on table "timetztmp" serially +DEBUG: using sorted GiST build +CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "timetzidx_b" on table "timetztmp" serially +DROP INDEX timetzidx_b; +RESET client_min_messages; SET enable_seqscan=off; UPDATE timetzcmp SET b=c FROM ( SELECT count(*) AS c FROM timetztmp WHERE a < '07:46:45 GMT+3'::timetz ) q WHERE r_id=1 ; UPDATE timetzcmp SET b=c FROM ( SELECT count(*) AS c FROM timetztmp WHERE a <= '07:46:45 GMT+3'::timetz ) q WHERE r_id=2 ; diff --git a/contrib/btree_gist/expected/uuid.out b/contrib/btree_gist/expected/uuid.out index a34b0246032..0f0f2960392 100644 --- a/contrib/btree_gist/expected/uuid.out +++ b/contrib/btree_gist/expected/uuid.out @@ -32,7 +32,14 @@ SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; 375 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); +DEBUG: building index "uuididx" on table "uuidtmp" serially +DEBUG: using sorted GiST build +CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); +DEBUG: building index "uuididx_b" on table "uuidtmp" serially +DROP INDEX uuididx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; count diff --git a/contrib/btree_gist/expected/varbit.out b/contrib/btree_gist/expected/varbit.out index ede36bc3ead..9cd41f4c9aa 100644 --- a/contrib/btree_gist/expected/varbit.out +++ b/contrib/btree_gist/expected/varbit.out @@ -32,7 +32,14 @@ SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; 50 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); +DEBUG: building index "varbitidx" on table "varbittmp" serially +DEBUG: using sorted GiST build +CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); +DEBUG: building index "varbitidx_b" on table "varbittmp" serially +DROP INDEX varbitidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM varbittmp WHERE a < '1110100111010'::varbit; count diff --git a/contrib/btree_gist/expected/varchar.out b/contrib/btree_gist/expected/varchar.out index d071d714cdd..0520eb47315 100644 --- a/contrib/btree_gist/expected/varchar.out +++ b/contrib/btree_gist/expected/varchar.out @@ -32,7 +32,14 @@ SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); 400 (1 row) +SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); +DEBUG: building index "vcharidx" on table "vchartmp" serially +DEBUG: using sorted GiST build +CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); +DEBUG: building index "vcharidx_b" on table "vchartmp" serially +DROP INDEX vcharidx_b; +RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM vchartmp WHERE a < '31b0'::varchar(32); count diff --git a/contrib/btree_gist/sql/bit.sql b/contrib/btree_gist/sql/bit.sql index a733042023f..53c67cf77ac 100644 --- a/contrib/btree_gist/sql/bit.sql +++ b/contrib/btree_gist/sql/bit.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM bittmp WHERE a >= '011011000100010111011000110000100'; SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; +SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); +CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX bitidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/bool.sql b/contrib/btree_gist/sql/bool.sql index 4c2a5392b6c..1a215abee1f 100644 --- a/contrib/btree_gist/sql/bool.sql +++ b/contrib/btree_gist/sql/bool.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM booltmp WHERE a >= true; SELECT count(*) FROM booltmp WHERE a > true; +SET client_min_messages = DEBUG1; CREATE INDEX boolidx ON booltmp USING gist ( a ); +CREATE INDEX boolidx_b ON booltmp USING gist ( a ) WITH (buffering=on); +DROP INDEX boolidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/bytea.sql b/contrib/btree_gist/sql/bytea.sql index 6885f5e56d5..fdfa0c345bd 100644 --- a/contrib/btree_gist/sql/bytea.sql +++ b/contrib/btree_gist/sql/bytea.sql @@ -17,7 +17,11 @@ SELECT count(*) FROM byteatmp WHERE a >= '31b0'; SELECT count(*) FROM byteatmp WHERE a > '31b0'; +SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); +CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX byteaidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/cash.sql b/contrib/btree_gist/sql/cash.sql index 4526cc4f0aa..0581b3593ef 100644 --- a/contrib/btree_gist/sql/cash.sql +++ b/contrib/btree_gist/sql/cash.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM moneytmp WHERE a > '22649.64'; SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); +CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); +DROP INDEX moneyidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/char.sql b/contrib/btree_gist/sql/char.sql index f6eb52e6724..234eabee3b8 100644 --- a/contrib/btree_gist/sql/char.sql +++ b/contrib/btree_gist/sql/char.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM chartmp WHERE a >= '31b0'::char(32); SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); +SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); +CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX charidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/cidr.sql b/contrib/btree_gist/sql/cidr.sql index 9bd77185b96..be2d22b079a 100644 --- a/contrib/btree_gist/sql/cidr.sql +++ b/contrib/btree_gist/sql/cidr.sql @@ -15,7 +15,11 @@ SELECT count(*) FROM cidrtmp WHERE a >= '121.111.63.82'; SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; +SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); +CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX cidridx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/date.sql b/contrib/btree_gist/sql/date.sql index f969ef0a08c..f007402bacc 100644 --- a/contrib/btree_gist/sql/date.sql +++ b/contrib/btree_gist/sql/date.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM datetmp WHERE a > '2001-02-13'; SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); +CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); +DROP INDEX dateidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/enum.sql b/contrib/btree_gist/sql/enum.sql index 476211e9795..d6dbcb42392 100644 --- a/contrib/btree_gist/sql/enum.sql +++ b/contrib/btree_gist/sql/enum.sql @@ -20,7 +20,11 @@ SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; +SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); +CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX enumidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/float4.sql b/contrib/btree_gist/sql/float4.sql index 3da1ce953c8..0e3eb49343f 100644 --- a/contrib/btree_gist/sql/float4.sql +++ b/contrib/btree_gist/sql/float4.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM float4tmp WHERE a > -179.0; SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); +CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX float4idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/float8.sql b/contrib/btree_gist/sql/float8.sql index e1e819b37f9..6a216dd6065 100644 --- a/contrib/btree_gist/sql/float8.sql +++ b/contrib/btree_gist/sql/float8.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM float8tmp WHERE a > -1890.0; SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); +CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX float8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/inet.sql b/contrib/btree_gist/sql/inet.sql index 4b8d354b00e..0339c853d38 100644 --- a/contrib/btree_gist/sql/inet.sql +++ b/contrib/btree_gist/sql/inet.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM inettmp WHERE a >= '89.225.196.191'; SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; +SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); +CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); +DROP INDEX inetidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int2.sql b/contrib/btree_gist/sql/int2.sql index 988518795fc..bf98ac65f83 100644 --- a/contrib/btree_gist/sql/int2.sql +++ b/contrib/btree_gist/sql/int2.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int2tmp WHERE a > 237; SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); +CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int2idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int4.sql b/contrib/btree_gist/sql/int4.sql index 659ab5ee24b..214993314af 100644 --- a/contrib/btree_gist/sql/int4.sql +++ b/contrib/btree_gist/sql/int4.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int4tmp WHERE a > 237; SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); +CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int4idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int8.sql b/contrib/btree_gist/sql/int8.sql index 51e55e9c14b..8a6c2a4bfd1 100644 --- a/contrib/btree_gist/sql/int8.sql +++ b/contrib/btree_gist/sql/int8.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM int8tmp WHERE a > 464571291354841; SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); +CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX int8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/interval.sql b/contrib/btree_gist/sql/interval.sql index 346d6adcb51..6f9b1d4a39a 100644 --- a/contrib/btree_gist/sql/interval.sql +++ b/contrib/btree_gist/sql/interval.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM intervaltmp WHERE a > '199 days 21:21:23'; SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); +CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); +DROP INDEX intervalidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/macaddr.sql b/contrib/btree_gist/sql/macaddr.sql index 85c271f7ce3..bccfc820ca4 100644 --- a/contrib/btree_gist/sql/macaddr.sql +++ b/contrib/btree_gist/sql/macaddr.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM macaddrtmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; +SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); +CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX macaddridx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/macaddr8.sql b/contrib/btree_gist/sql/macaddr8.sql index 61e7d7af405..2d0447a777b 100644 --- a/contrib/btree_gist/sql/macaddr8.sql +++ b/contrib/btree_gist/sql/macaddr8.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM macaddr8tmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; +SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); +CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); +DROP INDEX macaddr8idx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/numeric.sql b/contrib/btree_gist/sql/numeric.sql index dbb2f2f1838..55ecbcdadc5 100644 --- a/contrib/btree_gist/sql/numeric.sql +++ b/contrib/btree_gist/sql/numeric.sql @@ -40,7 +40,11 @@ SELECT count(*) FROM numerictmp WHERE a >= 0 ; SELECT count(*) FROM numerictmp WHERE a > 0 ; +SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); +CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); +DROP INDEX numericidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/oid.sql b/contrib/btree_gist/sql/oid.sql index c9358234ce9..bc9ee0cba34 100644 --- a/contrib/btree_gist/sql/oid.sql +++ b/contrib/btree_gist/sql/oid.sql @@ -15,7 +15,11 @@ SELECT count(*) FROM oidtmp WHERE oid >= 17; SELECT count(*) FROM oidtmp WHERE oid > 17; +SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); +CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); +DROP INDEX oididx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/text.sql b/contrib/btree_gist/sql/text.sql index 46597e731d6..52705a216d1 100644 --- a/contrib/btree_gist/sql/text.sql +++ b/contrib/btree_gist/sql/text.sql @@ -17,7 +17,11 @@ SELECT count(*) FROM texttmp WHERE a >= '31b0'; SELECT count(*) FROM texttmp WHERE a > '31b0'; +SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); +CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX textidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/time.sql b/contrib/btree_gist/sql/time.sql index 6104e7f61c8..61239452131 100644 --- a/contrib/btree_gist/sql/time.sql +++ b/contrib/btree_gist/sql/time.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM timetmp WHERE a > '10:57:11'; SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); +CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timeidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timestamp.sql b/contrib/btree_gist/sql/timestamp.sql index 95effebfc47..66a14f5ae51 100644 --- a/contrib/btree_gist/sql/timestamp.sql +++ b/contrib/btree_gist/sql/timestamp.sql @@ -18,7 +18,11 @@ SELECT count(*) FROM timestamptmp WHERE a > '2004-10-26 08:55:08'; SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); +CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timestampidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timestamptz.sql b/contrib/btree_gist/sql/timestamptz.sql index f70caa4a649..2a92f63fc4a 100644 --- a/contrib/btree_gist/sql/timestamptz.sql +++ b/contrib/btree_gist/sql/timestamptz.sql @@ -39,7 +39,11 @@ SELECT count(*) FROM timestamptztmp WHERE a > '2018-12-18 10:59:54 GMT+4'; SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3; +SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); +CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timestamptzidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timetz.sql b/contrib/btree_gist/sql/timetz.sql index 2fb725db747..bc79d134b8f 100644 --- a/contrib/btree_gist/sql/timetz.sql +++ b/contrib/btree_gist/sql/timetz.sql @@ -42,7 +42,11 @@ INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07: +SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); +CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); +DROP INDEX timetzidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/uuid.sql b/contrib/btree_gist/sql/uuid.sql index 3f7ad764e2d..7771bc0d828 100644 --- a/contrib/btree_gist/sql/uuid.sql +++ b/contrib/btree_gist/sql/uuid.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; +SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); +CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); +DROP INDEX uuididx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/varbit.sql b/contrib/btree_gist/sql/varbit.sql index e2a33b5a1b0..6d8243572bf 100644 --- a/contrib/btree_gist/sql/varbit.sql +++ b/contrib/btree_gist/sql/varbit.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM varbittmp WHERE a >= '1110100111010'; SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; +SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); +CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); +DROP INDEX varbitidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/varchar.sql b/contrib/btree_gist/sql/varchar.sql index 8087a17704e..59b77e0983c 100644 --- a/contrib/btree_gist/sql/varchar.sql +++ b/contrib/btree_gist/sql/varchar.sql @@ -16,7 +16,11 @@ SELECT count(*) FROM vchartmp WHERE a >= '31b0'::varchar(32); SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); +SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); +CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); +DROP INDEX vcharidx_b; +RESET client_min_messages; SET enable_seqscan=off; diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index baad28c09fa..8aff415167d 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -260,6 +260,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) /* * Sort all data, build the index from bottom up. */ + elog(DEBUG1, "using sorted GiST build"); buildstate.sortstate = tuplesort_begin_index_gist(heap, index, maintenance_work_mem, -- 2.33.1
v4-0002-Remove-DEBUG1-verification-of-the-fact-that-index.patch (text/x-diff, 40.7K) download From 1864133544814da3142edca0967ce1d26ad47c9a Mon Sep 17 00:00:00 2001 From: Andrey Borodin <x4m@flight.local> Date: Thu, 16 Dec 2021 12:59:32 +0500 Subject: [PATCH v4 2/4] Remove DEBUG1 verification of the fact that index was built with sorting --- contrib/btree_gist/expected/bit.out | 5 ----- contrib/btree_gist/expected/bool.out | 5 ----- contrib/btree_gist/expected/bytea.out | 5 ----- contrib/btree_gist/expected/cash.out | 5 ----- contrib/btree_gist/expected/char.out | 5 ----- contrib/btree_gist/expected/cidr.out | 5 ----- contrib/btree_gist/expected/date.out | 5 ----- contrib/btree_gist/expected/enum.out | 5 ----- contrib/btree_gist/expected/float4.out | 5 ----- contrib/btree_gist/expected/float8.out | 5 ----- contrib/btree_gist/expected/inet.out | 5 ----- contrib/btree_gist/expected/int2.out | 5 ----- contrib/btree_gist/expected/int4.out | 5 ----- contrib/btree_gist/expected/int8.out | 5 ----- contrib/btree_gist/expected/interval.out | 5 ----- contrib/btree_gist/expected/macaddr.out | 5 ----- contrib/btree_gist/expected/macaddr8.out | 5 ----- contrib/btree_gist/expected/numeric.out | 5 ----- contrib/btree_gist/expected/oid.out | 5 ----- contrib/btree_gist/expected/text.out | 5 ----- contrib/btree_gist/expected/time.out | 5 ----- contrib/btree_gist/expected/timestamp.out | 5 ----- contrib/btree_gist/expected/timestamptz.out | 5 ----- contrib/btree_gist/expected/timetz.out | 5 ----- contrib/btree_gist/expected/uuid.out | 5 ----- contrib/btree_gist/expected/varbit.out | 5 ----- contrib/btree_gist/expected/varchar.out | 5 ----- contrib/btree_gist/sql/bit.sql | 2 -- contrib/btree_gist/sql/bool.sql | 2 -- contrib/btree_gist/sql/bytea.sql | 2 -- contrib/btree_gist/sql/cash.sql | 2 -- contrib/btree_gist/sql/char.sql | 2 -- contrib/btree_gist/sql/cidr.sql | 2 -- contrib/btree_gist/sql/date.sql | 2 -- contrib/btree_gist/sql/enum.sql | 2 -- contrib/btree_gist/sql/float4.sql | 2 -- contrib/btree_gist/sql/float8.sql | 2 -- contrib/btree_gist/sql/inet.sql | 2 -- contrib/btree_gist/sql/int2.sql | 2 -- contrib/btree_gist/sql/int4.sql | 2 -- contrib/btree_gist/sql/int8.sql | 2 -- contrib/btree_gist/sql/interval.sql | 2 -- contrib/btree_gist/sql/macaddr.sql | 2 -- contrib/btree_gist/sql/macaddr8.sql | 2 -- contrib/btree_gist/sql/numeric.sql | 2 -- contrib/btree_gist/sql/oid.sql | 2 -- contrib/btree_gist/sql/text.sql | 2 -- contrib/btree_gist/sql/time.sql | 2 -- contrib/btree_gist/sql/timestamp.sql | 2 -- contrib/btree_gist/sql/timestamptz.sql | 2 -- contrib/btree_gist/sql/timetz.sql | 3 --- contrib/btree_gist/sql/uuid.sql | 2 -- contrib/btree_gist/sql/varbit.sql | 2 -- contrib/btree_gist/sql/varchar.sql | 2 -- 54 files changed, 190 deletions(-) diff --git a/contrib/btree_gist/expected/bit.out b/contrib/btree_gist/expected/bit.out index cb2297ce806..a635ce47e7a 100644 --- a/contrib/btree_gist/expected/bit.out +++ b/contrib/btree_gist/expected/bit.out @@ -32,14 +32,9 @@ SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; 350 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); -DEBUG: building index "bitidx" on table "bittmp" serially -DEBUG: using sorted GiST build CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); -DEBUG: building index "bitidx_b" on table "bittmp" serially DROP INDEX bitidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM bittmp WHERE a < '011011000100010111011000110000100'; count diff --git a/contrib/btree_gist/expected/bool.out b/contrib/btree_gist/expected/bool.out index f015cbf0623..37b37b5d874 100644 --- a/contrib/btree_gist/expected/bool.out +++ b/contrib/btree_gist/expected/bool.out @@ -32,14 +32,9 @@ SELECT count(*) FROM booltmp WHERE a > true; 0 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX boolidx ON booltmp USING gist ( a ); -DEBUG: building index "boolidx" on table "booltmp" serially -DEBUG: using sorted GiST build CREATE INDEX boolidx_b ON booltmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "boolidx_b" on table "booltmp" serially DROP INDEX boolidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM booltmp WHERE a < true; count diff --git a/contrib/btree_gist/expected/bytea.out b/contrib/btree_gist/expected/bytea.out index 170b48e1db9..76327fc02d6 100644 --- a/contrib/btree_gist/expected/bytea.out +++ b/contrib/btree_gist/expected/bytea.out @@ -33,14 +33,9 @@ SELECT count(*) FROM byteatmp WHERE a > '31b0'; 400 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); -DEBUG: building index "byteaidx" on table "byteatmp" serially -DEBUG: using sorted GiST build CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); -DEBUG: building index "byteaidx_b" on table "byteatmp" serially DROP INDEX byteaidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM byteatmp WHERE a < '31b0'::bytea; count diff --git a/contrib/btree_gist/expected/cash.out b/contrib/btree_gist/expected/cash.out index 868af70b22c..ad730d4560b 100644 --- a/contrib/btree_gist/expected/cash.out +++ b/contrib/btree_gist/expected/cash.out @@ -40,14 +40,9 @@ SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; $21,915.01 | $442.22 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); -DEBUG: building index "moneyidx" on table "moneytmp" serially -DEBUG: using sorted GiST build CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "moneyidx_b" on table "moneytmp" serially DROP INDEX moneyidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM moneytmp WHERE a < '22649.64'::money; count diff --git a/contrib/btree_gist/expected/char.out b/contrib/btree_gist/expected/char.out index 97316cbb06b..e17b1ae8153 100644 --- a/contrib/btree_gist/expected/char.out +++ b/contrib/btree_gist/expected/char.out @@ -32,14 +32,9 @@ SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); 400 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); -DEBUG: building index "charidx" on table "chartmp" serially -DEBUG: using sorted GiST build CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); -DEBUG: building index "charidx_b" on table "chartmp" serially DROP INDEX charidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM chartmp WHERE a < '31b0'::char(32); count diff --git a/contrib/btree_gist/expected/cidr.out b/contrib/btree_gist/expected/cidr.out index f15597c06a0..424444fd8e9 100644 --- a/contrib/btree_gist/expected/cidr.out +++ b/contrib/btree_gist/expected/cidr.out @@ -32,14 +32,9 @@ SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; 309 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); -DEBUG: building index "cidridx" on table "cidrtmp" serially -DEBUG: using sorted GiST build CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "cidridx_b" on table "cidrtmp" serially DROP INDEX cidridx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM cidrtmp WHERE a < '121.111.63.82'::cidr; count diff --git a/contrib/btree_gist/expected/date.out b/contrib/btree_gist/expected/date.out index 5c93d02209c..19037267a8b 100644 --- a/contrib/btree_gist/expected/date.out +++ b/contrib/btree_gist/expected/date.out @@ -40,14 +40,9 @@ SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; 03-24-2001 | 39 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); -DEBUG: building index "dateidx" on table "datetmp" serially -DEBUG: using sorted GiST build CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "dateidx_b" on table "datetmp" serially DROP INDEX dateidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM datetmp WHERE a < '2001-02-13'::date; count diff --git a/contrib/btree_gist/expected/enum.out b/contrib/btree_gist/expected/enum.out index d73ad33974d..2698341500a 100644 --- a/contrib/btree_gist/expected/enum.out +++ b/contrib/btree_gist/expected/enum.out @@ -46,14 +46,9 @@ SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; 230 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); -DEBUG: building index "enumidx" on table "enumtmp" serially -DEBUG: using sorted GiST build CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "enumidx_b" on table "enumtmp" serially DROP INDEX enumidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow; count diff --git a/contrib/btree_gist/expected/float4.out b/contrib/btree_gist/expected/float4.out index 5f4f1aa4ec5..e453e9dd062 100644 --- a/contrib/btree_gist/expected/float4.out +++ b/contrib/btree_gist/expected/float4.out @@ -40,14 +40,9 @@ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; -158.17741 | 20.822586 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); -DEBUG: building index "float4idx" on table "float4tmp" serially -DEBUG: using sorted GiST build CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "float4idx_b" on table "float4tmp" serially DROP INDEX float4idx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM float4tmp WHERE a < -179.0::float4; count diff --git a/contrib/btree_gist/expected/float8.out b/contrib/btree_gist/expected/float8.out index 4db0f7b8282..a039e90d0b5 100644 --- a/contrib/btree_gist/expected/float8.out +++ b/contrib/btree_gist/expected/float8.out @@ -40,14 +40,9 @@ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; -1769.73634 | 120.26366000000007 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); -DEBUG: building index "float8idx" on table "float8tmp" serially -DEBUG: using sorted GiST build CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "float8idx_b" on table "float8tmp" serially DROP INDEX float8idx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM float8tmp WHERE a < -1890.0::float8; count diff --git a/contrib/btree_gist/expected/inet.out b/contrib/btree_gist/expected/inet.out index 0847d3b7d12..5428631d779 100644 --- a/contrib/btree_gist/expected/inet.out +++ b/contrib/btree_gist/expected/inet.out @@ -32,14 +32,9 @@ SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; 386 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); -DEBUG: building index "inetidx" on table "inettmp" serially -DEBUG: using sorted GiST build CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "inetidx_b" on table "inettmp" serially DROP INDEX inetidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM inettmp WHERE a < '89.225.196.191'::inet; count diff --git a/contrib/btree_gist/expected/int2.out b/contrib/btree_gist/expected/int2.out index 9ad06a8dce0..bef4c500752 100644 --- a/contrib/btree_gist/expected/int2.out +++ b/contrib/btree_gist/expected/int2.out @@ -40,14 +40,9 @@ SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); -DEBUG: building index "int2idx" on table "int2tmp" serially -DEBUG: using sorted GiST build CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "int2idx_b" on table "int2tmp" serially DROP INDEX int2idx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int2tmp WHERE a < 237::int2; count diff --git a/contrib/btree_gist/expected/int4.out b/contrib/btree_gist/expected/int4.out index fdf143f32c3..b930d3ed319 100644 --- a/contrib/btree_gist/expected/int4.out +++ b/contrib/btree_gist/expected/int4.out @@ -40,14 +40,9 @@ SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; 228 | 9 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); -DEBUG: building index "int4idx" on table "int4tmp" serially -DEBUG: using sorted GiST build CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "int4idx_b" on table "int4tmp" serially DROP INDEX int4idx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int4tmp WHERE a < 237::int4; count diff --git a/contrib/btree_gist/expected/int8.out b/contrib/btree_gist/expected/int8.out index 532c4e5e704..0c4961e702c 100644 --- a/contrib/btree_gist/expected/int8.out +++ b/contrib/btree_gist/expected/int8.out @@ -40,14 +40,9 @@ SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' 478227196042750 | 13655904687909 (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); -DEBUG: building index "int8idx" on table "int8tmp" serially -DEBUG: using sorted GiST build CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "int8idx_b" on table "int8tmp" serially DROP INDEX int8idx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM int8tmp WHERE a < 464571291354841::int8; count diff --git a/contrib/btree_gist/expected/interval.out b/contrib/btree_gist/expected/interval.out index 12d50fdf581..7f5bd694eb7 100644 --- a/contrib/btree_gist/expected/interval.out +++ b/contrib/btree_gist/expected/interval.out @@ -40,14 +40,9 @@ SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21 @ 220 days 19 hours 5 mins 42 secs | @ 21 days -2 hours -15 mins -41 secs (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); -DEBUG: building index "intervalidx" on table "intervaltmp" serially -DEBUG: using sorted GiST build CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "intervalidx_b" on table "intervaltmp" serially DROP INDEX intervalidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM intervaltmp WHERE a < '199 days 21:21:23'::interval; count diff --git a/contrib/btree_gist/expected/macaddr.out b/contrib/btree_gist/expected/macaddr.out index 9634000618f..0ea068fd4c0 100644 --- a/contrib/btree_gist/expected/macaddr.out +++ b/contrib/btree_gist/expected/macaddr.out @@ -32,14 +32,9 @@ SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); -DEBUG: building index "macaddridx" on table "macaddrtmp" serially -DEBUG: using sorted GiST build CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "macaddridx_b" on table "macaddrtmp" serially DROP INDEX macaddridx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM macaddrtmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr; count diff --git a/contrib/btree_gist/expected/macaddr8.out b/contrib/btree_gist/expected/macaddr8.out index 910223cd3b2..c81858df3a1 100644 --- a/contrib/btree_gist/expected/macaddr8.out +++ b/contrib/btree_gist/expected/macaddr8.out @@ -32,14 +32,9 @@ SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; 540 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); -DEBUG: building index "macaddr8idx" on table "macaddr8tmp" serially -DEBUG: using sorted GiST build CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "macaddr8idx_b" on table "macaddr8tmp" serially DROP INDEX macaddr8idx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM macaddr8tmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr8; count diff --git a/contrib/btree_gist/expected/numeric.out b/contrib/btree_gist/expected/numeric.out index 8dce480c303..36fecadb4f9 100644 --- a/contrib/btree_gist/expected/numeric.out +++ b/contrib/btree_gist/expected/numeric.out @@ -94,14 +94,9 @@ SELECT count(*) FROM numerictmp WHERE a > 0 ; 576 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); -DEBUG: building index "numericidx" on table "numerictmp" serially -DEBUG: using sorted GiST build CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "numericidx_b" on table "numerictmp" serially DROP INDEX numericidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM numerictmp WHERE a < -1890.0; count diff --git a/contrib/btree_gist/expected/oid.out b/contrib/btree_gist/expected/oid.out index da27172609b..5cd54aba7f8 100644 --- a/contrib/btree_gist/expected/oid.out +++ b/contrib/btree_gist/expected/oid.out @@ -32,14 +32,9 @@ SELECT count(*) FROM oidtmp WHERE oid > 17; 983 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); -DEBUG: building index "oididx" on table "oidtmp" serially -DEBUG: using sorted GiST build CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); -DEBUG: building index "oididx_b" on table "oidtmp" serially DROP INDEX oididx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM oidtmp WHERE oid < 17; count diff --git a/contrib/btree_gist/expected/text.out b/contrib/btree_gist/expected/text.out index 2e760d14871..be9553c0360 100644 --- a/contrib/btree_gist/expected/text.out +++ b/contrib/btree_gist/expected/text.out @@ -33,14 +33,9 @@ SELECT count(*) FROM texttmp WHERE a > '31b0'; 400 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); -DEBUG: building index "textidx" on table "texttmp" serially -DEBUG: using sorted GiST build CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); -DEBUG: building index "textidx_b" on table "texttmp" serially DROP INDEX textidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM texttmp WHERE a < '31b0'::text; count diff --git a/contrib/btree_gist/expected/time.out b/contrib/btree_gist/expected/time.out index 9b81e58ed4d..68afe764f13 100644 --- a/contrib/btree_gist/expected/time.out +++ b/contrib/btree_gist/expected/time.out @@ -40,14 +40,9 @@ SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; 10:55:32 | @ 1 min 39 secs (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); -DEBUG: building index "timeidx" on table "timetmp" serially -DEBUG: using sorted GiST build CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "timeidx_b" on table "timetmp" serially DROP INDEX timeidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timetmp WHERE a < '10:57:11'::time; count diff --git a/contrib/btree_gist/expected/timestamp.out b/contrib/btree_gist/expected/timestamp.out index 8ea9897551c..a6bf88bede7 100644 --- a/contrib/btree_gist/expected/timestamp.out +++ b/contrib/btree_gist/expected/timestamp.out @@ -40,14 +40,9 @@ SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10- Mon Nov 29 20:12:43 2004 | @ 34 days 11 hours 17 mins 35 secs (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); -DEBUG: building index "timestampidx" on table "timestamptmp" serially -DEBUG: using sorted GiST build CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "timestampidx_b" on table "timestamptmp" serially DROP INDEX timestampidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timestamptmp WHERE a < '2004-10-26 08:55:08'::timestamp; count diff --git a/contrib/btree_gist/expected/timestamptz.out b/contrib/btree_gist/expected/timestamptz.out index 2ba0dcd7ede..ade4ddd885d 100644 --- a/contrib/btree_gist/expected/timestamptz.out +++ b/contrib/btree_gist/expected/timestamptz.out @@ -100,14 +100,9 @@ SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> ' Thu Jan 24 12:28:12 2019 PST | @ 37 days 7 hours 28 mins 18 secs (3 rows) -SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); -DEBUG: building index "timestamptzidx" on table "timestamptztmp" serially -DEBUG: using sorted GiST build CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "timestamptzidx_b" on table "timestamptztmp" serially DROP INDEX timestamptzidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM timestamptztmp WHERE a < '2018-12-18 10:59:54 GMT+3'::timestamptz; count diff --git a/contrib/btree_gist/expected/timetz.out b/contrib/btree_gist/expected/timetz.out index 6c855bfcd64..259dc12b7f2 100644 --- a/contrib/btree_gist/expected/timetz.out +++ b/contrib/btree_gist/expected/timetz.out @@ -18,14 +18,9 @@ INSERT INTO timetzcmp (r_id,a) SELECT 22,count(*) FROM timetztmp WHERE a <= '07: INSERT INTO timetzcmp (r_id,a) SELECT 23,count(*) FROM timetztmp WHERE a = '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 24,count(*) FROM timetztmp WHERE a >= '07:46:45 GMT+4'; INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07:46:45 GMT+4'; -SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); -DEBUG: building index "timetzidx" on table "timetztmp" serially -DEBUG: using sorted GiST build CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "timetzidx_b" on table "timetztmp" serially DROP INDEX timetzidx_b; -RESET client_min_messages; SET enable_seqscan=off; UPDATE timetzcmp SET b=c FROM ( SELECT count(*) AS c FROM timetztmp WHERE a < '07:46:45 GMT+3'::timetz ) q WHERE r_id=1 ; UPDATE timetzcmp SET b=c FROM ( SELECT count(*) AS c FROM timetztmp WHERE a <= '07:46:45 GMT+3'::timetz ) q WHERE r_id=2 ; diff --git a/contrib/btree_gist/expected/uuid.out b/contrib/btree_gist/expected/uuid.out index 0f0f2960392..3e78dca1e8a 100644 --- a/contrib/btree_gist/expected/uuid.out +++ b/contrib/btree_gist/expected/uuid.out @@ -32,14 +32,9 @@ SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; 375 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); -DEBUG: building index "uuididx" on table "uuidtmp" serially -DEBUG: using sorted GiST build CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); -DEBUG: building index "uuididx_b" on table "uuidtmp" serially DROP INDEX uuididx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; count diff --git a/contrib/btree_gist/expected/varbit.out b/contrib/btree_gist/expected/varbit.out index 9cd41f4c9aa..867572df3d5 100644 --- a/contrib/btree_gist/expected/varbit.out +++ b/contrib/btree_gist/expected/varbit.out @@ -32,14 +32,9 @@ SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; 50 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); -DEBUG: building index "varbitidx" on table "varbittmp" serially -DEBUG: using sorted GiST build CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); -DEBUG: building index "varbitidx_b" on table "varbittmp" serially DROP INDEX varbitidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM varbittmp WHERE a < '1110100111010'::varbit; count diff --git a/contrib/btree_gist/expected/varchar.out b/contrib/btree_gist/expected/varchar.out index 0520eb47315..ba8aa3f2bd7 100644 --- a/contrib/btree_gist/expected/varchar.out +++ b/contrib/btree_gist/expected/varchar.out @@ -32,14 +32,9 @@ SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); 400 (1 row) -SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); -DEBUG: building index "vcharidx" on table "vchartmp" serially -DEBUG: using sorted GiST build CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); -DEBUG: building index "vcharidx_b" on table "vchartmp" serially DROP INDEX vcharidx_b; -RESET client_min_messages; SET enable_seqscan=off; SELECT count(*) FROM vchartmp WHERE a < '31b0'::varchar(32); count diff --git a/contrib/btree_gist/sql/bit.sql b/contrib/btree_gist/sql/bit.sql index 53c67cf77ac..e214efc36ec 100644 --- a/contrib/btree_gist/sql/bit.sql +++ b/contrib/btree_gist/sql/bit.sql @@ -16,11 +16,9 @@ SELECT count(*) FROM bittmp WHERE a >= '011011000100010111011000110000100'; SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; -SET client_min_messages = DEBUG1; CREATE INDEX bitidx ON bittmp USING GIST ( a ); CREATE INDEX bitidx_b ON bittmp USING GIST ( a ) WITH (buffering=on); DROP INDEX bitidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/bool.sql b/contrib/btree_gist/sql/bool.sql index 1a215abee1f..0ea88edf7d4 100644 --- a/contrib/btree_gist/sql/bool.sql +++ b/contrib/btree_gist/sql/bool.sql @@ -16,11 +16,9 @@ SELECT count(*) FROM booltmp WHERE a >= true; SELECT count(*) FROM booltmp WHERE a > true; -SET client_min_messages = DEBUG1; CREATE INDEX boolidx ON booltmp USING gist ( a ); CREATE INDEX boolidx_b ON booltmp USING gist ( a ) WITH (buffering=on); DROP INDEX boolidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/bytea.sql b/contrib/btree_gist/sql/bytea.sql index fdfa0c345bd..233adccba93 100644 --- a/contrib/btree_gist/sql/bytea.sql +++ b/contrib/btree_gist/sql/bytea.sql @@ -17,11 +17,9 @@ SELECT count(*) FROM byteatmp WHERE a >= '31b0'; SELECT count(*) FROM byteatmp WHERE a > '31b0'; -SET client_min_messages = DEBUG1; CREATE INDEX byteaidx ON byteatmp USING GIST ( a ); CREATE INDEX byteaidx_b ON byteatmp USING GIST ( a ) WITH (buffering=on); DROP INDEX byteaidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/cash.sql b/contrib/btree_gist/sql/cash.sql index 0581b3593ef..b1a66eba6c8 100644 --- a/contrib/btree_gist/sql/cash.sql +++ b/contrib/btree_gist/sql/cash.sql @@ -18,11 +18,9 @@ SELECT count(*) FROM moneytmp WHERE a > '22649.64'; SELECT a, a <-> '21472.79' FROM moneytmp ORDER BY a <-> '21472.79' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX moneyidx ON moneytmp USING gist ( a ); CREATE INDEX moneyidx_b ON moneytmp USING gist ( a ) WITH (buffering=on); DROP INDEX moneyidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/char.sql b/contrib/btree_gist/sql/char.sql index 234eabee3b8..3bf954bb53d 100644 --- a/contrib/btree_gist/sql/char.sql +++ b/contrib/btree_gist/sql/char.sql @@ -16,11 +16,9 @@ SELECT count(*) FROM chartmp WHERE a >= '31b0'::char(32); SELECT count(*) FROM chartmp WHERE a > '31b0'::char(32); -SET client_min_messages = DEBUG1; CREATE INDEX charidx ON chartmp USING GIST ( a ); CREATE INDEX charidx_b ON chartmp USING GIST ( a ) WITH (buffering=on); DROP INDEX charidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/cidr.sql b/contrib/btree_gist/sql/cidr.sql index be2d22b079a..9d753cc3adb 100644 --- a/contrib/btree_gist/sql/cidr.sql +++ b/contrib/btree_gist/sql/cidr.sql @@ -15,11 +15,9 @@ SELECT count(*) FROM cidrtmp WHERE a >= '121.111.63.82'; SELECT count(*) FROM cidrtmp WHERE a > '121.111.63.82'; -SET client_min_messages = DEBUG1; CREATE INDEX cidridx ON cidrtmp USING gist ( a ); CREATE INDEX cidridx_b ON cidrtmp USING gist ( a ) WITH (buffering=on); DROP INDEX cidridx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/date.sql b/contrib/btree_gist/sql/date.sql index f007402bacc..e988313f509 100644 --- a/contrib/btree_gist/sql/date.sql +++ b/contrib/btree_gist/sql/date.sql @@ -18,11 +18,9 @@ SELECT count(*) FROM datetmp WHERE a > '2001-02-13'; SELECT a, a <-> '2001-02-13' FROM datetmp ORDER BY a <-> '2001-02-13' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX dateidx ON datetmp USING gist ( a ); CREATE INDEX dateidx_b ON datetmp USING gist ( a ) WITH (buffering=on); DROP INDEX dateidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/enum.sql b/contrib/btree_gist/sql/enum.sql index d6dbcb42392..b4f23996017 100644 --- a/contrib/btree_gist/sql/enum.sql +++ b/contrib/btree_gist/sql/enum.sql @@ -20,11 +20,9 @@ SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; -SET client_min_messages = DEBUG1; CREATE INDEX enumidx ON enumtmp USING gist ( a ); CREATE INDEX enumidx_b ON enumtmp USING gist ( a ) WITH (buffering=on); DROP INDEX enumidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/float4.sql b/contrib/btree_gist/sql/float4.sql index 0e3eb49343f..0b6b4b19ceb 100644 --- a/contrib/btree_gist/sql/float4.sql +++ b/contrib/btree_gist/sql/float4.sql @@ -18,11 +18,9 @@ SELECT count(*) FROM float4tmp WHERE a > -179.0; SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX float4idx ON float4tmp USING gist ( a ); CREATE INDEX float4idx_b ON float4tmp USING gist ( a ) WITH (buffering=on); DROP INDEX float4idx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/float8.sql b/contrib/btree_gist/sql/float8.sql index 6a216dd6065..e8d173d7260 100644 --- a/contrib/btree_gist/sql/float8.sql +++ b/contrib/btree_gist/sql/float8.sql @@ -18,11 +18,9 @@ SELECT count(*) FROM float8tmp WHERE a > -1890.0; SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX float8idx ON float8tmp USING gist ( a ); CREATE INDEX float8idx_b ON float8tmp USING gist ( a ) WITH (buffering=on); DROP INDEX float8idx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/inet.sql b/contrib/btree_gist/sql/inet.sql index 0339c853d38..a6f3f481787 100644 --- a/contrib/btree_gist/sql/inet.sql +++ b/contrib/btree_gist/sql/inet.sql @@ -16,11 +16,9 @@ SELECT count(*) FROM inettmp WHERE a >= '89.225.196.191'; SELECT count(*) FROM inettmp WHERE a > '89.225.196.191'; -SET client_min_messages = DEBUG1; CREATE INDEX inetidx ON inettmp USING gist ( a ); CREATE INDEX inetidx_b ON inettmp USING gist ( a ) WITH (buffering=on); DROP INDEX inetidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int2.sql b/contrib/btree_gist/sql/int2.sql index bf98ac65f83..8492d4ab63a 100644 --- a/contrib/btree_gist/sql/int2.sql +++ b/contrib/btree_gist/sql/int2.sql @@ -18,11 +18,9 @@ SELECT count(*) FROM int2tmp WHERE a > 237; SELECT a, a <-> '237' FROM int2tmp ORDER BY a <-> '237' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX int2idx ON int2tmp USING gist ( a ); CREATE INDEX int2idx_b ON int2tmp USING gist ( a ) WITH (buffering=on); DROP INDEX int2idx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int4.sql b/contrib/btree_gist/sql/int4.sql index 214993314af..575369e52bf 100644 --- a/contrib/btree_gist/sql/int4.sql +++ b/contrib/btree_gist/sql/int4.sql @@ -18,11 +18,9 @@ SELECT count(*) FROM int4tmp WHERE a > 237; SELECT a, a <-> '237' FROM int4tmp ORDER BY a <-> '237' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX int4idx ON int4tmp USING gist ( a ); CREATE INDEX int4idx_b ON int4tmp USING gist ( a ) WITH (buffering=on); DROP INDEX int4idx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/int8.sql b/contrib/btree_gist/sql/int8.sql index 8a6c2a4bfd1..574e5fe6dd0 100644 --- a/contrib/btree_gist/sql/int8.sql +++ b/contrib/btree_gist/sql/int8.sql @@ -18,11 +18,9 @@ SELECT count(*) FROM int8tmp WHERE a > 464571291354841; SELECT a, a <-> '464571291354841' FROM int8tmp ORDER BY a <-> '464571291354841' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX int8idx ON int8tmp USING gist ( a ); CREATE INDEX int8idx_b ON int8tmp USING gist ( a ) WITH (buffering=on); DROP INDEX int8idx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/interval.sql b/contrib/btree_gist/sql/interval.sql index 6f9b1d4a39a..33bd95c4a58 100644 --- a/contrib/btree_gist/sql/interval.sql +++ b/contrib/btree_gist/sql/interval.sql @@ -18,11 +18,9 @@ SELECT count(*) FROM intervaltmp WHERE a > '199 days 21:21:23'; SELECT a, a <-> '199 days 21:21:23' FROM intervaltmp ORDER BY a <-> '199 days 21:21:23' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX intervalidx ON intervaltmp USING gist ( a ); CREATE INDEX intervalidx_b ON intervaltmp USING gist ( a ) WITH (buffering=on); DROP INDEX intervalidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/macaddr.sql b/contrib/btree_gist/sql/macaddr.sql index bccfc820ca4..57c24093ece 100644 --- a/contrib/btree_gist/sql/macaddr.sql +++ b/contrib/btree_gist/sql/macaddr.sql @@ -16,11 +16,9 @@ SELECT count(*) FROM macaddrtmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddrtmp WHERE a > '22:00:5c:e5:9b:0d'; -SET client_min_messages = DEBUG1; CREATE INDEX macaddridx ON macaddrtmp USING gist ( a ); CREATE INDEX macaddridx_b ON macaddrtmp USING gist ( a ) WITH (buffering=on); DROP INDEX macaddridx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/macaddr8.sql b/contrib/btree_gist/sql/macaddr8.sql index 2d0447a777b..0d3c93d4fc2 100644 --- a/contrib/btree_gist/sql/macaddr8.sql +++ b/contrib/btree_gist/sql/macaddr8.sql @@ -16,11 +16,9 @@ SELECT count(*) FROM macaddr8tmp WHERE a >= '22:00:5c:e5:9b:0d'; SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; -SET client_min_messages = DEBUG1; CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); CREATE INDEX macaddr8idx_b ON macaddr8tmp USING gist ( a ) WITH (buffering=on); DROP INDEX macaddr8idx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/numeric.sql b/contrib/btree_gist/sql/numeric.sql index 55ecbcdadc5..12f7425f679 100644 --- a/contrib/btree_gist/sql/numeric.sql +++ b/contrib/btree_gist/sql/numeric.sql @@ -40,11 +40,9 @@ SELECT count(*) FROM numerictmp WHERE a >= 0 ; SELECT count(*) FROM numerictmp WHERE a > 0 ; -SET client_min_messages = DEBUG1; CREATE INDEX numericidx ON numerictmp USING gist ( a ); CREATE INDEX numericidx_b ON numerictmp USING gist ( a ) WITH (buffering=on); DROP INDEX numericidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/oid.sql b/contrib/btree_gist/sql/oid.sql index bc9ee0cba34..69f7bd74635 100644 --- a/contrib/btree_gist/sql/oid.sql +++ b/contrib/btree_gist/sql/oid.sql @@ -15,11 +15,9 @@ SELECT count(*) FROM oidtmp WHERE oid >= 17; SELECT count(*) FROM oidtmp WHERE oid > 17; -SET client_min_messages = DEBUG1; CREATE INDEX oididx ON oidtmp USING gist ( oid ); CREATE INDEX oididx_b ON oidtmp USING gist ( oid ) WITH (buffering=on); DROP INDEX oididx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/text.sql b/contrib/btree_gist/sql/text.sql index 52705a216d1..2527735509d 100644 --- a/contrib/btree_gist/sql/text.sql +++ b/contrib/btree_gist/sql/text.sql @@ -17,11 +17,9 @@ SELECT count(*) FROM texttmp WHERE a >= '31b0'; SELECT count(*) FROM texttmp WHERE a > '31b0'; -SET client_min_messages = DEBUG1; CREATE INDEX textidx ON texttmp USING GIST ( a ); CREATE INDEX textidx_b ON texttmp USING GIST ( a ) WITH (buffering=on); DROP INDEX textidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/time.sql b/contrib/btree_gist/sql/time.sql index 61239452131..6300a977b15 100644 --- a/contrib/btree_gist/sql/time.sql +++ b/contrib/btree_gist/sql/time.sql @@ -18,11 +18,9 @@ SELECT count(*) FROM timetmp WHERE a > '10:57:11'; SELECT a, a <-> '10:57:11' FROM timetmp ORDER BY a <-> '10:57:11' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX timeidx ON timetmp USING gist ( a ); CREATE INDEX timeidx_b ON timetmp USING gist ( a ) WITH (buffering=on); DROP INDEX timeidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timestamp.sql b/contrib/btree_gist/sql/timestamp.sql index 66a14f5ae51..6ca72a007a9 100644 --- a/contrib/btree_gist/sql/timestamp.sql +++ b/contrib/btree_gist/sql/timestamp.sql @@ -18,11 +18,9 @@ SELECT count(*) FROM timestamptmp WHERE a > '2004-10-26 08:55:08'; SELECT a, a <-> '2004-10-26 08:55:08' FROM timestamptmp ORDER BY a <-> '2004-10-26 08:55:08' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX timestampidx ON timestamptmp USING gist ( a ); CREATE INDEX timestampidx_b ON timestamptmp USING gist ( a ) WITH (buffering=on); DROP INDEX timestampidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timestamptz.sql b/contrib/btree_gist/sql/timestamptz.sql index 2a92f63fc4a..c154f3d815e 100644 --- a/contrib/btree_gist/sql/timestamptz.sql +++ b/contrib/btree_gist/sql/timestamptz.sql @@ -39,11 +39,9 @@ SELECT count(*) FROM timestamptztmp WHERE a > '2018-12-18 10:59:54 GMT+4'; SELECT a, a <-> '2018-12-18 10:59:54 GMT+2' FROM timestamptztmp ORDER BY a <-> '2018-12-18 10:59:54 GMT+2' LIMIT 3; -SET client_min_messages = DEBUG1; CREATE INDEX timestamptzidx ON timestamptztmp USING gist ( a ); CREATE INDEX timestamptzidx_b ON timestamptztmp USING gist ( a ) WITH (buffering=on); DROP INDEX timestamptzidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/timetz.sql b/contrib/btree_gist/sql/timetz.sql index bc79d134b8f..6262a133d0e 100644 --- a/contrib/btree_gist/sql/timetz.sql +++ b/contrib/btree_gist/sql/timetz.sql @@ -41,12 +41,9 @@ INSERT INTO timetzcmp (r_id,a) SELECT 24,count(*) FROM timetztmp WHERE a >= '07: INSERT INTO timetzcmp (r_id,a) SELECT 25,count(*) FROM timetztmp WHERE a > '07:46:45 GMT+4'; - -SET client_min_messages = DEBUG1; CREATE INDEX timetzidx ON timetztmp USING gist ( a ); CREATE INDEX timetzidx_b ON timetztmp USING gist ( a ) WITH (buffering=on); DROP INDEX timetzidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/uuid.sql b/contrib/btree_gist/sql/uuid.sql index 7771bc0d828..c140a3a0cc6 100644 --- a/contrib/btree_gist/sql/uuid.sql +++ b/contrib/btree_gist/sql/uuid.sql @@ -16,11 +16,9 @@ SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; -SET client_min_messages = DEBUG1; CREATE INDEX uuididx ON uuidtmp USING gist ( a ); CREATE INDEX uuididx_b ON uuidtmp USING gist ( a ) WITH (buffering=on); DROP INDEX uuididx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/varbit.sql b/contrib/btree_gist/sql/varbit.sql index 6d8243572bf..6f001d37621 100644 --- a/contrib/btree_gist/sql/varbit.sql +++ b/contrib/btree_gist/sql/varbit.sql @@ -16,11 +16,9 @@ SELECT count(*) FROM varbittmp WHERE a >= '1110100111010'; SELECT count(*) FROM varbittmp WHERE a > '1110100111010'; -SET client_min_messages = DEBUG1; CREATE INDEX varbitidx ON varbittmp USING GIST ( a ); CREATE INDEX varbitidx_b ON varbittmp USING GIST ( a ) WITH (buffering=on); DROP INDEX varbitidx_b; -RESET client_min_messages; SET enable_seqscan=off; diff --git a/contrib/btree_gist/sql/varchar.sql b/contrib/btree_gist/sql/varchar.sql index 59b77e0983c..608581ace5a 100644 --- a/contrib/btree_gist/sql/varchar.sql +++ b/contrib/btree_gist/sql/varchar.sql @@ -16,11 +16,9 @@ SELECT count(*) FROM vchartmp WHERE a >= '31b0'::varchar(32); SELECT count(*) FROM vchartmp WHERE a > '31b0'::varchar(32); -SET client_min_messages = DEBUG1; CREATE INDEX vcharidx ON vchartmp USING GIST ( text(a) ); CREATE INDEX vcharidx_b ON vchartmp USING GIST ( text(a) ) WITH (buffering=on); DROP INDEX vcharidx_b; -RESET client_min_messages; SET enable_seqscan=off; -- 2.33.1
v4-0003-Fix-varlen-keys-sorting.patch (text/x-diff, 3.3K) download From 5632e6f3e03cf8ba58220b8dc9b2d0212d9dec43 Mon Sep 17 00:00:00 2001 From: Andrey Borodin <amborodin@acm.org> Date: Fri, 7 May 2021 17:47:23 +0500 Subject: [PATCH v4 3/4] Fix varlen keys sorting --- contrib/btree_gist/btree_bit.c | 6 ++++-- contrib/btree_gist/btree_bytea.c | 6 ++++-- contrib/btree_gist/btree_numeric.c | 6 ++++-- contrib/btree_gist/btree_text.c | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c index 61b2eecfd59..947e3b63acd 100644 --- a/contrib/btree_gist/btree_bit.c +++ b/contrib/btree_gist/btree_bit.c @@ -214,10 +214,12 @@ gbt_bit_penalty(PG_FUNCTION_ARGS) static int gbt_bit_sort_build_cmp(Datum a, Datum b, SortSupport ssup) { + GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a)); + GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b)); /* Use byteacmp(), like gbt_bitcmp() does */ return DatumGetInt32(DirectFunctionCall2(byteacmp, - PointerGetDatum(a), - PointerGetDatum(b))); + PointerGetDatum(ra.lower), + PointerGetDatum(rb.lower))); } /* diff --git a/contrib/btree_gist/btree_bytea.c b/contrib/btree_gist/btree_bytea.c index a2abfb7d7c2..9ffb3b9a5d8 100644 --- a/contrib/btree_gist/btree_bytea.c +++ b/contrib/btree_gist/btree_bytea.c @@ -173,9 +173,11 @@ gbt_bytea_penalty(PG_FUNCTION_ARGS) static int gbt_bytea_sort_build_cmp(Datum a, Datum b, SortSupport ssup) { + GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a)); + GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b)); return DatumGetInt32(DirectFunctionCall2(byteacmp, - PointerGetDatum(a), - PointerGetDatum(b))); + PointerGetDatum(ra.lower), + PointerGetDatum(rb.lower))); } /* diff --git a/contrib/btree_gist/btree_numeric.c b/contrib/btree_gist/btree_numeric.c index face4e2b3af..76cd5d7380f 100644 --- a/contrib/btree_gist/btree_numeric.c +++ b/contrib/btree_gist/btree_numeric.c @@ -232,9 +232,11 @@ gbt_numeric_picksplit(PG_FUNCTION_ARGS) static int gbt_numeric_sort_build_cmp(Datum a, Datum b, SortSupport ssup) { + GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a)); + GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b)); return DatumGetInt32(DirectFunctionCall2(numeric_cmp, - PointerGetDatum(a), - PointerGetDatum(b))); + PointerGetDatum(ra.lower), + PointerGetDatum(rb.lower))); } /* diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 01b1bda2f66..2deb8dd76fc 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -244,10 +244,12 @@ gbt_text_penalty(PG_FUNCTION_ARGS) static int gbt_text_sort_build_cmp(Datum a, Datum b, SortSupport ssup) { + GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a)); + GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b)); return DatumGetInt32(DirectFunctionCall2Coll(bttextcmp, ssup->ssup_collation, - PointerGetDatum(a), - PointerGetDatum(b))); + PointerGetDatum(ra.lower), + PointerGetDatum(rb.lower))); } /* -- 2.33.1
v4-0004-Review-notes-from-Emre-Hasegeli.patch (text/x-diff, 2.0K) download From 08c6780374668d5e9fe3be547b7f6465b55d0451 Mon Sep 17 00:00:00 2001 From: Andrey Borodin <x4m@flight.local> Date: Thu, 16 Dec 2021 14:17:15 +0500 Subject: [PATCH v4 4/4] Review notes from Emre Hasegeli --- contrib/btree_gist/btree_bit.c | 12 +++++++++++- contrib/btree_gist/btree_text.c | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c index 947e3b63acd..6b70bcbd0db 100644 --- a/contrib/btree_gist/btree_bit.c +++ b/contrib/btree_gist/btree_bit.c @@ -216,7 +216,17 @@ gbt_bit_sort_build_cmp(Datum a, Datum b, SortSupport ssup) { GBT_VARKEY_R ra = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(a)); GBT_VARKEY_R rb = gbt_var_key_readable((GBT_VARKEY *) PG_DETOAST_DATUM(b)); - /* Use byteacmp(), like gbt_bitcmp() does */ + /* + * Use byteacmp(), like gbt_bitcmp() does. + * In btree_gist, the *_cmp() function operates on non-leaf values, and + * *_lt(), *_gt() et al operate on leaf values. For all other datatypes, + * the leaf and non-leaf representation is the same, but for bit/varbit, + * the non-leaf representation is different. + * The leaf representation is VarBit, and non-leaf is just the bits without + * the 'bit_len' field. That's why it is indeed correct for gbt_bitcmp() to + * just use byteacmp(), whereas gbt_bitlt() et al compares the 'bit_len' + * field separately. + */ return DatumGetInt32(DirectFunctionCall2(byteacmp, PointerGetDatum(ra.lower), PointerGetDatum(rb.lower))); diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 2deb8dd76fc..69490788cec 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -260,6 +260,10 @@ gbt_text_sortsupport(PG_FUNCTION_ARGS) { SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); + /* + * Text has abbreviation routines in varlena.c, but we don't try to use + * them here. Maybe later. + */ ssup->comparator = gbt_text_sort_build_cmp; ssup->abbrev_converter = NULL; ssup->abbrev_abort = NULL; -- 2.33.1
^ permalink raw reply [nested|flat] 111+ messages in thread


end of thread, other threads:[~2021-12-16 09:18 UTC | newest]

Thread overview: 111+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2019-08-26 07:59 Yet another fast GiST build Andrey Borodin <x4mmm@yandex-team.ru>
2019-08-26 10:27 ` Darafei "Komяpa" Praliaskouski <me@komzpa.net>
2019-08-26 10:47 ` Heikki Linnakangas <hlinnaka@iki.fi>
2019-08-29 22:47 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2019-08-29 23:33   ` Peter Geoghegan <pg@bowt.ie>
2019-08-30 03:21     ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2019-08-30 03:28       ` Peter Geoghegan <pg@bowt.ie>
2019-09-01 17:20         ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2019-08-30 11:44   ` Andrey Borodin <x4mmm@yandex-team.ru>
2019-09-01 10:53     ` Andrey Borodin <x4mmm@yandex-team.ru>
2019-09-08 08:54       ` Andrey Borodin <x4mmm@yandex-team.ru>
2019-12-01 02:06         ` Michael Paquier <michael@paquier.xyz>
2019-12-30 06:43           ` Andrey Borodin <x4mmm@yandex-team.ru>
2020-02-19 07:00             ` Thomas Munro <thomas.munro@gmail.com>
2020-02-19 21:14               ` Thomas Munro <thomas.munro@gmail.com>
2020-02-23 20:58                 ` Thomas Munro <thomas.munro@gmail.com>
2020-02-24 08:50                   ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-02-29 12:13                     ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-03-01 10:09                       ` Darafei "Komяpa" Praliaskouski <me@komzpa.net>
2020-04-03 03:17                         ` Yuri Astrakhan <yuriastrakhan@gmail.com>
2020-04-05 09:48                           ` Darafei "Komяpa" Praliaskouski <me@komzpa.net>
2020-09-09 10:28                     ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-09-09 12:09                       ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-09 12:20                         ` Darafei "Komяpa" Praliaskouski <me@komzpa.net>
2020-09-09 15:39                           ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-09 15:39                           ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-09 16:50                             ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-09-10 12:43                               ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-11 06:02                                 ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-09-15 11:36                                   ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-15 16:46                                     ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-09-15 17:07                                       ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-16 07:27                                         ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-09-16 08:20                                           ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-16 09:09                                           ` Kyotaro Horiguchi <horikyota.ntt@gmail.com>
2020-09-17 08:38                                     ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-17 08:43                                       ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-09-17 12:11                                       ` Justin Pryzby <pryzby@telsasoft.com>
2020-09-20 20:41                                     ` Tom Lane <tgl@sss.pgh.pa.us>
2020-09-20 21:10                                       ` Tom Lane <tgl@sss.pgh.pa.us>
2020-09-20 22:44                                         ` Justin Pryzby <pryzby@telsasoft.com>
2020-09-20 23:06                                           ` Tom Lane <tgl@sss.pgh.pa.us>
2020-09-21 08:08                                             ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-21 08:45                                               ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-21 09:06                                                 ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-09-21 12:15                                                   ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-21 13:29                                                     ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-09-21 14:19                                                       ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-09-28 08:12                                                         ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-29 18:04                                                           ` Andrey M. Borodin <x4mmm@yandex-team.ru>
2020-09-29 20:31                                                             ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-10-06 11:05                                                               ` Pavel Borisov <pashkin.elfe@gmail.com>
2020-10-06 13:19                                                                 ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-10-06 14:29                                                                 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-10-06 15:29                                                                   ` Pavel Borisov <pashkin.elfe@gmail.com>
2020-10-07 12:27                                                             ` Andrey Borodin <x4mmm@yandex-team.ru>
2020-10-07 12:38                                                               ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-10-21 17:13                                                                 ` Andrey Borodin <x4mmm@yandex-team.ru>
2020-10-21 17:35                                                                   ` Pavel Borisov <pashkin.elfe@gmail.com>
2020-10-27 11:43                                                                   ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-10-30 18:20                                                                     ` Andrey Borodin <x4mmm@yandex-team.ru>
2020-11-02 14:45                                                                       ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-11-05 17:11                                                                         ` Andrey Borodin <x4mmm@yandex-team.ru>
2020-11-05 17:20                                                                           ` Justin Pryzby <pryzby@telsasoft.com>
2020-11-07 15:42                                                                             ` Andrey Borodin <x4mmm@yandex-team.ru>
2020-12-07 10:05                                                           ` Andrey Borodin <x4mmm@yandex-team.ru>
2020-12-07 18:56                                                             ` Peter Geoghegan <pg@bowt.ie>
2020-12-09 09:47                                                               ` Andrey Borodin <x4mmm@yandex-team.ru>
2020-12-10 10:16                                                                 ` Andrey Borodin <x4mmm@yandex-team.ru>
2021-01-12 13:49                                                                   ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-01-12 16:19                                                                     ` Andrey Borodin <x4mmm@yandex-team.ru>
2021-01-13 08:41                                                                       ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-01-13 09:46                                                                         ` Andrey Borodin <x4mmm@yandex-team.ru>
2021-01-13 10:34                                                                           ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-01-13 11:53                                                                             ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-01-13 18:04                                                                               ` Heikki Linnakangas <hlinnaka@gmail.com>
2021-01-13 18:16                                                                                 ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-01-13 16:17                                                                           ` Tom Lane <tgl@sss.pgh.pa.us>
2021-01-17 20:50                                                                       ` Tom Lane <tgl@sss.pgh.pa.us>
2021-01-17 22:35                                                                         ` Peter Geoghegan <pg@bowt.ie>
2021-01-17 22:52                                                                           ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-01-17 23:04                                                                             ` Peter Geoghegan <pg@bowt.ie>
2021-01-17 23:10                                                                               ` Peter Geoghegan <pg@bowt.ie>
2021-01-18 12:40                                                                                 ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-03-08 14:41                                                                             ` Ibrar Ahmed <ibrar.ahmad@gmail.com>
2021-03-08 15:59                                                                               ` Peter Geoghegan <pg@bowt.ie>
2021-03-08 16:15                                                                                 ` Ibrar Ahmed <ibrar.ahmad@gmail.com>
2021-03-08 17:06                                                                                   ` Andrey Borodin <x4mmm@yandex-team.ru>
2021-04-07 06:00                                                                                     ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-04-07 10:23                                                                                       ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-04-07 11:56                                                                                       ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-04-07 12:12                                                                                         ` Andrey Borodin <x4mmm@yandex-team.ru>
2021-04-07 13:18                                                                                           ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-04-07 19:41                                                                                             ` Andrey Borodin <x4mmm@yandex-team.ru>
2021-04-07 20:18                                                                                               ` Heikki Linnakangas <hlinnaka@iki.fi>
2021-05-07 13:05                                                                                                 ` Andrey Borodin <x4mmm@yandex-team.ru>
2021-07-05 06:27                                                                                                   ` Emre Hasegeli <emre@hasegeli.com>
2021-11-17 13:33                                                                                                     ` Daniel Gustafsson <daniel@yesql.se>
2021-11-25 13:38                                                                                                       ` Andrey Borodin <x4mmm@yandex-team.ru>
2021-12-16 09:18                                                                                                     ` Andrey Borodin <x4mmm@yandex-team.ru>
2021-01-13 23:47                                                                     ` Peter Geoghegan <pg@bowt.ie>
2021-01-14 09:52                                                                       ` Andrey Borodin <x4mmm@yandex-team.ru>
2021-01-15 05:24                                                                     ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2021-01-15 17:05                                                                       ` Andrey Borodin <x4mmm@yandex-team.ru>
2020-09-28 06:53                                                       ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-21 14:20                                               ` Tom Lane <tgl@sss.pgh.pa.us>
2020-09-21 11:56                                             ` Heikki Linnakangas <hlinnaka@iki.fi>
2020-09-10 10:21                     ` Oleg Bartunov <obartunov@postgrespro.ru>
2020-09-10 11:19                       ` Pavel Borisov <pashkin.elfe@gmail.com>
2019-09-01 17:17     ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2020-08-18 15:57 ` Pavel Borisov <pashkin.elfe@gmail.com>

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox