Split out tiebreaker comparisons from comparetup_* functions
authorJohn Naylor <john.naylor@postgresql.org>
Wed, 16 Aug 2023 10:15:07 +0000 (17:15 +0700)
committerJohn Naylor <john.naylor@postgresql.org>
Wed, 16 Aug 2023 10:15:07 +0000 (17:15 +0700)
Previously, if a specialized comparator found equal datum1 keys,
the "comparetup" function would repeat the comparison on the
datum before proceeding with the unabbreviated first key
and/or additional sort keys.

Move comparing additional sort keys into "tiebreak" functions so
that specialized comparators can call these directly if needed,
avoiding duplicate work.

Reviewed by David Rowley

Discussion: https://postgr.es/m/CAFBsxsGaVfUrjTghpf%3DkDBYY%3DjWx1PN-fuusVe7Vw5s0XqGdGw%40mail.gmail.com

src/backend/utils/sort/tuplesort.c
src/backend/utils/sort/tuplesortvariants.c
src/include/utils/tuplesort.h

index e5a4e5b371e3527a1c47dd728dde7993ef7a5bd3..c7a6c03f975757a17aa758bb8da0f7b28a1a175f 100644 (file)
@@ -485,9 +485,6 @@ static void tuplesort_updatemax(Tuplesortstate *state);
  * is to try to sort two tuples without having to follow the pointers to the
  * comparator or the tuple.
  *
- * XXX: For now, these fall back to comparator functions that will compare the
- * leading datum a second time.
- *
  * XXX: For now, there is no specialization for cases where datum1 is
  * authoritative and we don't even need to fall back to a callback at all (that
  * would be true for types like int4/int8/timestamp/date, but not true for
@@ -513,7 +510,7 @@ qsort_tuple_unsigned_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
    if (state->base.onlyKey != NULL)
        return 0;
 
-   return state->base.comparetup(a, b, state);
+   return state->base.comparetup_tiebreak(a, b, state);
 }
 
 #if SIZEOF_DATUM >= 8
@@ -537,7 +534,7 @@ qsort_tuple_signed_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
    if (state->base.onlyKey != NULL)
        return 0;
 
-   return state->base.comparetup(a, b, state);
+   return state->base.comparetup_tiebreak(a, b, state);
 }
 #endif
 
@@ -561,7 +558,7 @@ qsort_tuple_int32_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
    if (state->base.onlyKey != NULL)
        return 0;
 
-   return state->base.comparetup(a, b, state);
+   return state->base.comparetup_tiebreak(a, b, state);
 }
 
 /*
index eb6cfcfd002134a228800df9e5a2111219ec66a2..84442a93c5a5ea0ad42c02146c08f5fbf2921427 100644 (file)
@@ -47,26 +47,36 @@ static void removeabbrev_datum(Tuplesortstate *state, SortTuple *stups,
                               int count);
 static int comparetup_heap(const SortTuple *a, const SortTuple *b,
                            Tuplesortstate *state);
+static int comparetup_heap_tiebreak(const SortTuple *a, const SortTuple *b,
+                                    Tuplesortstate *state);
 static void writetup_heap(Tuplesortstate *state, LogicalTape *tape,
                          SortTuple *stup);
 static void readtup_heap(Tuplesortstate *state, SortTuple *stup,
                         LogicalTape *tape, unsigned int len);
 static int comparetup_cluster(const SortTuple *a, const SortTuple *b,
                               Tuplesortstate *state);
+static int comparetup_cluster_tiebreak(const SortTuple *a, const SortTuple *b,
+                                       Tuplesortstate *state);
 static void writetup_cluster(Tuplesortstate *state, LogicalTape *tape,
                             SortTuple *stup);
 static void readtup_cluster(Tuplesortstate *state, SortTuple *stup,
                            LogicalTape *tape, unsigned int tuplen);
 static int comparetup_index_btree(const SortTuple *a, const SortTuple *b,
                                   Tuplesortstate *state);
+static int comparetup_index_btree_tiebreak(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_hash_tiebreak(const SortTuple *a, const SortTuple *b,
+                                          Tuplesortstate *state);
 static void writetup_index(Tuplesortstate *state, LogicalTape *tape,
                           SortTuple *stup);
 static void readtup_index(Tuplesortstate *state, SortTuple *stup,
                          LogicalTape *tape, unsigned int len);
 static int comparetup_datum(const SortTuple *a, const SortTuple *b,
                             Tuplesortstate *state);
+static int comparetup_datum_tiebreak(const SortTuple *a, const SortTuple *b,
+                                     Tuplesortstate *state);
 static void writetup_datum(Tuplesortstate *state, LogicalTape *tape,
                           SortTuple *stup);
 static void readtup_datum(Tuplesortstate *state, SortTuple *stup,
@@ -165,6 +175,7 @@ tuplesort_begin_heap(TupleDesc tupDesc,
 
    base->removeabbrev = removeabbrev_heap;
    base->comparetup = comparetup_heap;
+   base->comparetup_tiebreak = comparetup_heap_tiebreak;
    base->writetup = writetup_heap;
    base->readtup = readtup_heap;
    base->haveDatum1 = true;
@@ -242,6 +253,7 @@ tuplesort_begin_cluster(TupleDesc tupDesc,
 
    base->removeabbrev = removeabbrev_cluster;
    base->comparetup = comparetup_cluster;
+   base->comparetup_tiebreak = comparetup_cluster_tiebreak;
    base->writetup = writetup_cluster;
    base->readtup = readtup_cluster;
    base->freestate = freestate_cluster;
@@ -351,6 +363,7 @@ tuplesort_begin_index_btree(Relation heapRel,
 
    base->removeabbrev = removeabbrev_index;
    base->comparetup = comparetup_index_btree;
+   base->comparetup_tiebreak = comparetup_index_btree_tiebreak;
    base->writetup = writetup_index;
    base->readtup = readtup_index;
    base->haveDatum1 = true;
@@ -431,6 +444,7 @@ tuplesort_begin_index_hash(Relation heapRel,
 
    base->removeabbrev = removeabbrev_index;
    base->comparetup = comparetup_index_hash;
+   base->comparetup_tiebreak = comparetup_index_hash_tiebreak;
    base->writetup = writetup_index;
    base->readtup = readtup_index;
    base->haveDatum1 = true;
@@ -476,6 +490,7 @@ tuplesort_begin_index_gist(Relation heapRel,
 
    base->removeabbrev = removeabbrev_index;
    base->comparetup = comparetup_index_btree;
+   base->comparetup_tiebreak = comparetup_index_btree_tiebreak;
    base->writetup = writetup_index;
    base->readtup = readtup_index;
    base->haveDatum1 = true;
@@ -546,6 +561,7 @@ tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation,
 
    base->removeabbrev = removeabbrev_datum;
    base->comparetup = comparetup_datum;
+   base->comparetup_tiebreak = comparetup_datum_tiebreak;
    base->writetup = writetup_datum;
    base->readtup = readtup_datum;
    base->haveDatum1 = true;
@@ -931,16 +947,7 @@ comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
 {
    TuplesortPublic *base = TuplesortstateGetPublic(state);
    SortSupport sortKey = base->sortKeys;
-   HeapTupleData ltup;
-   HeapTupleData rtup;
-   TupleDesc   tupDesc;
-   int         nkey;
    int32       compare;
-   AttrNumber  attno;
-   Datum       datum1,
-               datum2;
-   bool        isnull1,
-               isnull2;
 
 
    /* Compare the leading sort key */
@@ -951,6 +958,25 @@ comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
        return compare;
 
    /* Compare additional sort keys */
+   return comparetup_heap_tiebreak(a, b, state);
+}
+
+static int
+comparetup_heap_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
+{
+   TuplesortPublic *base = TuplesortstateGetPublic(state);
+   SortSupport sortKey = base->sortKeys;
+   HeapTupleData ltup;
+   HeapTupleData rtup;
+   TupleDesc   tupDesc;
+   int         nkey;
+   int32       compare;
+   AttrNumber  attno;
+   Datum       datum1,
+               datum2;
+   bool        isnull1,
+               isnull2;
+
    ltup.t_len = ((MinimalTuple) a->tuple)->t_len + MINIMAL_TUPLE_OFFSET;
    ltup.t_data = (HeapTupleHeader) ((char *) a->tuple - MINIMAL_TUPLE_OFFSET);
    rtup.t_len = ((MinimalTuple) b->tuple)->t_len + MINIMAL_TUPLE_OFFSET;
@@ -1061,6 +1087,27 @@ removeabbrev_cluster(Tuplesortstate *state, SortTuple *stups, int count)
 static int
 comparetup_cluster(const SortTuple *a, const SortTuple *b,
                   Tuplesortstate *state)
+{
+   TuplesortPublic *base = TuplesortstateGetPublic(state);
+   SortSupport sortKey = base->sortKeys;
+   int32       compare;
+
+   /* Compare the leading sort key, if it's simple */
+   if (base->haveDatum1)
+   {
+       compare = ApplySortComparator(a->datum1, a->isnull1,
+                                     b->datum1, b->isnull1,
+                                     sortKey);
+       if (compare != 0)
+           return compare;
+   }
+
+   return comparetup_cluster_tiebreak(a, b, state);
+}
+
+static int
+comparetup_cluster_tiebreak(const SortTuple *a, const SortTuple *b,
+                           Tuplesortstate *state)
 {
    TuplesortPublic *base = TuplesortstateGetPublic(state);
    TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
@@ -1069,13 +1116,12 @@ comparetup_cluster(const SortTuple *a, const SortTuple *b,
    HeapTuple   rtup;
    TupleDesc   tupDesc;
    int         nkey;
-   int32       compare;
+   int32       compare = 0;
    Datum       datum1,
                datum2;
    bool        isnull1,
                isnull2;
 
-   /* Be prepared to compare additional sort keys */
    ltup = (HeapTuple) a->tuple;
    rtup = (HeapTuple) b->tuple;
    tupDesc = arg->tupDesc;
@@ -1083,12 +1129,6 @@ comparetup_cluster(const SortTuple *a, const SortTuple *b,
    /* Compare the leading sort key, if it's simple */
    if (base->haveDatum1)
    {
-       compare = ApplySortComparator(a->datum1, a->isnull1,
-                                     b->datum1, b->isnull1,
-                                     sortKey);
-       if (compare != 0)
-           return compare;
-
        if (sortKey->abbrev_converter)
        {
            AttrNumber  leading = arg->indexInfo->ii_IndexAttrNumbers[0];
@@ -1269,6 +1309,25 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b,
     * treatment for equal keys at the end.
     */
    TuplesortPublic *base = TuplesortstateGetPublic(state);
+   SortSupport sortKey = base->sortKeys;
+   int32       compare;
+
+   /* 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 */
+   return comparetup_index_btree_tiebreak(a, b, state);
+}
+
+static int
+comparetup_index_btree_tiebreak(const SortTuple *a, const SortTuple *b,
+                               Tuplesortstate *state)
+{
+   TuplesortPublic *base = TuplesortstateGetPublic(state);
    TuplesortIndexBTreeArg *arg = (TuplesortIndexBTreeArg *) base->arg;
    SortSupport sortKey = base->sortKeys;
    IndexTuple  tuple1;
@@ -1283,15 +1342,6 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b,
    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;
@@ -1467,6 +1517,19 @@ comparetup_index_hash(const SortTuple *a, const SortTuple *b,
    return 0;
 }
 
+/*
+ * Sorting for hash indexes only uses one sort key, so this shouldn't ever be
+ * called. It's only here for consistency.
+ */
+static int
+comparetup_index_hash_tiebreak(const SortTuple *a, const SortTuple *b,
+                              Tuplesortstate *state)
+{
+   Assert(false);
+
+   return 0;
+}
+
 static void
 writetup_index(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
 {
@@ -1526,8 +1589,16 @@ comparetup_datum(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
    if (compare != 0)
        return compare;
 
-   /* if we have abbreviations, then "tuple" has the original value */
+   return comparetup_datum_tiebreak(a, b, state);
+}
 
+static int
+comparetup_datum_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
+{
+   TuplesortPublic *base = TuplesortstateGetPublic(state);
+   int32       compare = 0;
+
+   /* if we have abbreviations, then "tuple" has the original value */
    if (base->sortKeys->abbrev_converter)
        compare = ApplySortAbbrevFullComparator(PointerGetDatum(a->tuple), a->isnull1,
                                                PointerGetDatum(b->tuple), b->isnull1,
index af057b6358efa81135168110e58f039e2c43bace..3f71c70f175553d3ea3998e45ba0f6bc686e9b14 100644 (file)
@@ -162,6 +162,13 @@ typedef struct
     */
    SortTupleComparator comparetup;
 
+   /*
+    * Fall back to the full tuple for comparison, but only compare the first
+    * sortkey if it was abbreviated. Otherwise, only compare second and later
+    * sortkeys.
+    */
+   SortTupleComparator comparetup_tiebreak;
+
    /*
     * Alter datum1 representation in the SortTuple's array back from the
     * abbreviated key to the first column value.