From 8d587233c593d510edc50783af303831837f6b32 Mon Sep 17 00:00:00 2001 From: Matthias van de Meent Date: Thu, 21 Apr 2022 16:22:07 +0200 Subject: [PATCH v6 7/8] Add specialization to btree index creation. This was an oversight that is corrected easily; but an oversight nonetheless. This increases the (re)build performance of indexes by another few percents. --- src/backend/utils/sort/multisort.c | 22 +++ src/backend/utils/sort/tuplesortvariants.c | 146 +---------------- .../utils/sort/tuplesortvariants_nbts.h | 150 ++++++++++++++++++ src/include/access/nbtree.h | 18 +++ 4 files changed, 196 insertions(+), 140 deletions(-) create mode 100644 src/backend/utils/sort/multisort.c create mode 100644 src/backend/utils/sort/tuplesortvariants_nbts.h diff --git a/src/backend/utils/sort/multisort.c b/src/backend/utils/sort/multisort.c new file mode 100644 index 0000000000..3c0871faa6 --- /dev/null +++ b/src/backend/utils/sort/multisort.c @@ -0,0 +1,22 @@ +#include "postgres.h" + +#include "utils/sortsupport.h" +#include "utils/tuplesort.h" + +struct MultiSortData { + Tuplesortstate *buffer; + void *inner; + +}; + + +/* + * MultiSort { + * TupleSort buffer Contains sorted run of tuples received from + * innerstate. May have dropped tuples since. + * void* innerstate Inner state of multisort scheme. + * SortSlot low_watermark all tuples up to X have been received) + * SortSlot high_watermark (no tuples after X have been received) + * } + * sort_begin(MultiSort state, + */ diff --git a/src/backend/utils/sort/tuplesortvariants.c b/src/backend/utils/sort/tuplesortvariants.c index 2933020dcc..06fdeb64c6 100644 --- a/src/backend/utils/sort/tuplesortvariants.c +++ b/src/backend/utils/sort/tuplesortvariants.c @@ -57,8 +57,6 @@ static void writetup_cluster(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup); static void readtup_cluster(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len); -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 void writetup_index(Tuplesortstate *state, LogicalTape *tape, @@ -130,6 +128,10 @@ typedef struct int datumTypeLen; } TuplesortDatumArg; +#define NBT_SPECIALIZE_FILE "../../backend/utils/sort/tuplesortvariants_nbts.h" +#include "access/nbtree_specialize.h" +#undef NBT_SPECIALIZE_FILE + Tuplesortstate * tuplesort_begin_heap(TupleDesc tupDesc, int nkeys, AttrNumber *attNums, @@ -350,7 +352,7 @@ tuplesort_begin_index_btree(Relation heapRel, PARALLEL_SORT(coordinate)); base->removeabbrev = removeabbrev_index; - base->comparetup = comparetup_index_btree; + base->comparetup = NBT_SPECIALIZE_NAME(comparetup_index_btree, indexRel); base->writetup = writetup_index; base->readtup = readtup_index; base->haveDatum1 = true; @@ -475,7 +477,7 @@ tuplesort_begin_index_gist(Relation heapRel, base->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); base->removeabbrev = removeabbrev_index; - base->comparetup = comparetup_index_btree; + base->comparetup = NBT_SPECIALIZE_NAME(comparetup_index_btree, indexRel); base->writetup = writetup_index; base->readtup = readtup_index; base->haveDatum1 = true; @@ -1245,142 +1247,6 @@ removeabbrev_index(Tuplesortstate *state, SortTuple *stups, int count) } } -static int -comparetup_index_btree(const SortTuple *a, const SortTuple *b, - Tuplesortstate *state) -{ - /* - * This is similar to comparetup_heap(), but expects index tuples. There - * is also special handling for enforcing uniqueness, and special - * treatment for equal keys at the end. - */ - TuplesortPublic *base = TuplesortstateGetPublic(state); - TuplesortIndexBTreeArg *arg = (TuplesortIndexBTreeArg *) base->arg; - SortSupport sortKey = base->sortKeys; - IndexTuple tuple1; - IndexTuple tuple2; - int keysz; - TupleDesc tupDes; - bool equal_hasnull = false; - int nkey; - int32 compare; - Datum datum1, - datum2; - bool isnull1, - isnull2; - - - /* Compare the leading sort key */ - compare = ApplySortComparator(a->datum1, a->isnull1, - b->datum1, b->isnull1, - sortKey); - if (compare != 0) - return compare; - - /* Compare additional sort keys */ - tuple1 = (IndexTuple) a->tuple; - tuple2 = (IndexTuple) b->tuple; - keysz = base->nKeys; - tupDes = RelationGetDescr(arg->index.indexRel); - - if (sortKey->abbrev_converter) - { - datum1 = index_getattr(tuple1, 1, tupDes, &isnull1); - datum2 = index_getattr(tuple2, 1, tupDes, &isnull2); - - compare = ApplySortAbbrevFullComparator(datum1, isnull1, - datum2, isnull2, - sortKey); - if (compare != 0) - return compare; - } - - /* they are equal, so we only need to examine one null flag */ - if (a->isnull1) - equal_hasnull = true; - - sortKey++; - for (nkey = 2; nkey <= keysz; nkey++, sortKey++) - { - datum1 = index_getattr(tuple1, nkey, tupDes, &isnull1); - datum2 = index_getattr(tuple2, nkey, tupDes, &isnull2); - - compare = ApplySortComparator(datum1, isnull1, - datum2, isnull2, - sortKey); - if (compare != 0) - return compare; /* done when we find unequal attributes */ - - /* they are equal, so we only need to examine one null flag */ - if (isnull1) - equal_hasnull = true; - } - - /* - * If btree has asked us to enforce uniqueness, complain if two equal - * tuples are detected (unless there was at least one NULL field and NULLS - * NOT DISTINCT was not set). - * - * It is sufficient to make the test here, because if two tuples are equal - * they *must* get compared at some stage of the sort --- otherwise the - * sort algorithm wouldn't have checked whether one must appear before the - * other. - */ - if (arg->enforceUnique && !(!arg->uniqueNullsNotDistinct && equal_hasnull)) - { - Datum values[INDEX_MAX_KEYS]; - bool isnull[INDEX_MAX_KEYS]; - char *key_desc; - - /* - * Some rather brain-dead implementations of qsort (such as the one in - * QNX 4) will sometimes call the comparison routine to compare a - * value to itself, but we always use our own implementation, which - * does not. - */ - Assert(tuple1 != tuple2); - - index_deform_tuple(tuple1, tupDes, values, isnull); - - key_desc = BuildIndexValueDescription(arg->index.indexRel, values, isnull); - - ereport(ERROR, - (errcode(ERRCODE_UNIQUE_VIOLATION), - errmsg("could not create unique index \"%s\"", - RelationGetRelationName(arg->index.indexRel)), - key_desc ? errdetail("Key %s is duplicated.", key_desc) : - errdetail("Duplicate keys exist."), - errtableconstraint(arg->index.heapRel, - RelationGetRelationName(arg->index.indexRel)))); - } - - /* - * If key values are equal, we sort on ItemPointer. This is required for - * btree indexes, since heap TID is treated as an implicit last key - * attribute in order to ensure that all keys in the index are physically - * unique. - */ - { - 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 int comparetup_index_hash(const SortTuple *a, const SortTuple *b, Tuplesortstate *state) diff --git a/src/backend/utils/sort/tuplesortvariants_nbts.h b/src/backend/utils/sort/tuplesortvariants_nbts.h new file mode 100644 index 0000000000..d52d34c749 --- /dev/null +++ b/src/backend/utils/sort/tuplesortvariants_nbts.h @@ -0,0 +1,150 @@ +#ifndef NBTS_SPECIALIZING_DEFAULT + +static int NBTS_FUNCTION(comparetup_index_btree)(const SortTuple *a, + const SortTuple *b, + Tuplesortstate *state); + +static int +NBTS_FUNCTION(comparetup_index_btree)(const SortTuple *a, const SortTuple *b, + Tuplesortstate *state) +{ + /* + * This is similar to comparetup_heap(), but expects index tuples. There + * is also special handling for enforcing uniqueness, and special + * treatment for equal keys at the end. + */ + TuplesortPublic *base = TuplesortstateGetPublic(state); + TuplesortIndexBTreeArg *arg = (TuplesortIndexBTreeArg *) base->arg; + SortSupport sortKey = base->sortKeys; + IndexTuple tuple1; + IndexTuple tuple2; + int keysz; + TupleDesc tupDes; + bool equal_hasnull = false; + int nkey; + int32 compare; + nbts_attiterdeclare(tuple1); + nbts_attiterdeclare(tuple2); + + /* Compare the leading sort key */ + compare = ApplySortComparator(a->datum1, a->isnull1, + b->datum1, b->isnull1, + sortKey); + if (compare != 0) + return compare; + + /* Compare additional sort keys */ + tuple1 = (IndexTuple) a->tuple; + tuple2 = (IndexTuple) b->tuple; + keysz = base->nKeys; + tupDes = RelationGetDescr(arg->index.indexRel); + + if (!sortKey->abbrev_converter) + { + nkey = 2; + sortKey++; + } + else + nkey = 1; + + if (a->isnull1) + equal_hasnull = true; + + nbts_attiterinit(tuple1, nkey, tupDes); + nbts_attiterinit(tuple2, nkey, tupDes); + + nbts_foreachattr(nkey, keysz) + { + Datum datum1, + datum2; + datum1 = nbts_attiter_nextattdatum(tuple1, tupDes); + datum2 = nbts_attiter_nextattdatum(tuple2, tupDes); + + if (nbts_attiter_attnum == 1) + { + compare = ApplySortAbbrevFullComparator(datum1, nbts_attiter_curattisnull(tuple1), + datum2, nbts_attiter_curattisnull(tuple2), + sortKey); + } + else + { + compare = ApplySortComparator(datum1, nbts_attiter_curattisnull(tuple1), + datum2, nbts_attiter_curattisnull(tuple2), + sortKey); + } + + if (compare != 0) + return compare; + + if (nbts_attiter_curattisnull(tuple1)) + equal_hasnull = true; + + sortKey++; + } + + /* + * If btree has asked us to enforce uniqueness, complain if two equal + * tuples are detected (unless there was at least one NULL field and NULLS + * NOT DISTINCT was not set). + * + * It is sufficient to make the test here, because if two tuples are equal + * they *must* get compared at some stage of the sort --- otherwise the + * sort algorithm wouldn't have checked whether one must appear before the + * other. + */ + if (arg->enforceUnique && !(!arg->uniqueNullsNotDistinct && equal_hasnull)) + { + Datum values[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + char *key_desc; + + /* + * Some rather brain-dead implementations of qsort (such as the one in + * QNX 4) will sometimes call the comparison routine to compare a + * value to itself, but we always use our own implementation, which + * does not. + */ + Assert(tuple1 != tuple2); + + index_deform_tuple(tuple1, tupDes, values, isnull); + + key_desc = BuildIndexValueDescription(arg->index.indexRel, values, isnull); + + ereport(ERROR, + (errcode(ERRCODE_UNIQUE_VIOLATION), + errmsg("could not create unique index \"%s\"", + RelationGetRelationName(arg->index.indexRel)), + key_desc ? errdetail("Key %s is duplicated.", key_desc) : + errdetail("Duplicate keys exist."), + errtableconstraint(arg->index.heapRel, + RelationGetRelationName(arg->index.indexRel)))); + } + + /* + * If key values are equal, we sort on ItemPointer. This is required for + * btree indexes, since heap TID is treated as an implicit last key + * attribute in order to ensure that all keys in the index are physically + * unique. + */ + { + 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; +} + +#endif diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index 92894e4ea7..11116b47ca 100644 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -1170,6 +1170,24 @@ do { \ ) \ ) +#define NBT_SPECIALIZE_NAME(name, rel) \ +( \ + IndexRelationGetNumberOfKeyAttributes(rel) == 1 ? ( \ + NBTS_MAKE_NAME(name, NBTS_TYPE_SINGLE_COLUMN) \ + ) \ + : \ + ( \ + TupleDescAttr(RelationGetDescr(rel), \ + IndexRelationGetNumberOfKeyAttributes(rel) - 1)->attcacheoff > 0 ? ( \ + NBTS_MAKE_NAME(name, NBTS_TYPE_CACHED) \ + ) \ + : \ + ( \ + NBTS_MAKE_NAME(name, NBTS_TYPE_UNCACHED) \ + ) \ + ) \ +) + #else /* not defined NBTS_ENABLED */ #define nbt_opt_specialize(rel) -- 2.30.2