Add GiST and btree sortsupport routines for range types
authorHeikki Linnakangas <[email protected]>
Wed, 2 Apr 2025 16:51:28 +0000 (19:51 +0300)
committerHeikki Linnakangas <[email protected]>
Wed, 2 Apr 2025 16:51:28 +0000 (19:51 +0300)
For GiST, having a sortsupport function allows building the index
using the "sorted build" method, which is much faster.

For b-tree, the sortsupport routine doesn't give any new
functionality, but speeds up sorting a tiny bit. The difference is not
very significant, about 2% in cursory testing on my laptop, because
the range type comparison function has quite a lot of overhead from
detoasting. In any case, since we have the function for GiST anyway,
we might as well register it for the btree opfamily too.

Author: Bernd Helmle <[email protected]>
Discussion: https://www.postgresql.org/message-id/64d324ce2a6d535d3f0f3baeeea7b25beff82ce4[email protected]

src/backend/utils/adt/rangetypes.c
src/include/catalog/catversion.h
src/include/catalog/pg_amproc.dat
src/include/catalog/pg_proc.dat

index 5f9fb23871aacd5ec608de06b3ddbfb5684c05ac..66cc0acf4a71244b56c1263caad2a21c9403134a 100644 (file)
@@ -43,6 +43,7 @@
 #include "utils/date.h"
 #include "utils/lsyscache.h"
 #include "utils/rangetypes.h"
+#include "utils/sortsupport.h"
 #include "utils/timestamp.h"
 
 
@@ -57,6 +58,7 @@ typedef struct RangeIOData
 
 static RangeIOData *get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
                                      IOFuncSelector func);
+static int range_fast_cmp(Datum a, Datum b, SortSupport ssup);
 static char range_parse_flags(const char *flags_str);
 static bool range_parse(const char *string, char *flags, char **lbound_str,
                        char **ubound_str, Node *escontext);
@@ -1290,6 +1292,68 @@ range_cmp(PG_FUNCTION_ARGS)
    PG_RETURN_INT32(cmp);
 }
 
+/* Sort support strategy routine */
+Datum
+range_sortsupport(PG_FUNCTION_ARGS)
+{
+   SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
+
+   ssup->comparator = range_fast_cmp;
+   ssup->ssup_extra = NULL;
+
+   PG_RETURN_VOID();
+}
+
+/* like range_cmp, but uses the new sortsupport interface */
+static int
+range_fast_cmp(Datum a, Datum b, SortSupport ssup)
+{
+   RangeType  *range_a = DatumGetRangeTypeP(a);
+   RangeType  *range_b = DatumGetRangeTypeP(b);
+   TypeCacheEntry *typcache;
+   RangeBound  lower1,
+               lower2;
+   RangeBound  upper1,
+               upper2;
+   bool        empty1,
+               empty2;
+   int         cmp;
+
+   /* cache the range info between calls */
+   if (ssup->ssup_extra == NULL)
+   {
+       Assert(RangeTypeGetOid(range_a) == RangeTypeGetOid(range_b));
+       ssup->ssup_extra =
+           lookup_type_cache(RangeTypeGetOid(range_a), TYPECACHE_RANGE_INFO);
+   }
+   typcache = ssup->ssup_extra;
+
+   range_deserialize(typcache, range_a, &lower1, &upper1, &empty1);
+   range_deserialize(typcache, range_b, &lower2, &upper2, &empty2);
+
+   /* For b-tree use, empty ranges sort before all else */
+   if (empty1 && empty2)
+       cmp = 0;
+   else if (empty1)
+       cmp = -1;
+   else if (empty2)
+       cmp = 1;
+   else
+   {
+       cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+       if (cmp == 0)
+           cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+   }
+
+   if ((Datum) range_a != a)
+       pfree(range_a);
+   if ((Datum) range_b != b)
+       pfree(range_b);
+
+   return cmp;
+}
+
+
 /* inequality operators using the range_cmp function */
 Datum
 range_lt(PG_FUNCTION_ARGS)
index 8b4ee1fa13f08c9d34db2697bd0c9988be299179..0c9b8ac9de15b3ead02402dec141f151de1a9a82 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202504012
+#define CATALOG_VERSION_NO 202504021
 
 #endif
index 19100482ba495799307d60a3dd6dbcb552bc7c21..41056171059e513fd2299a6481b696b07343d9ec 100644 (file)
   amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
 { amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
   amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
+  amprocrighttype => 'anyrange', amprocnum => '2',
+  amproc => 'range_sortsupport' },
 { amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
   amprocrighttype => 'anymultirange', amprocnum => '1',
   amproc => 'multirange_cmp' },
 { amprocfamily => 'gist/range_ops', amproclefttype => 'anyrange',
   amprocrighttype => 'anyrange', amprocnum => '7',
   amproc => 'range_gist_same' },
+{ amprocfamily => 'gist/range_ops', amproclefttype => 'anyrange',
+  amprocrighttype => 'anyrange', amprocnum => '11',
+  amproc => 'range_sortsupport' },
 { amprocfamily => 'gist/range_ops', amproclefttype => 'any',
   amprocrighttype => 'any', amprocnum => '12',
   amproc => 'gist_stratnum_common' },
index 6b57b7e18d9890149228298f605d0bf0d2c4fb66..4962d611f3169c892f92d25db0d334b6726eb383 100644 (file)
 { oid => '3870', descr => 'less-equal-greater',
   proname => 'range_cmp', prorettype => 'int4',
   proargtypes => 'anyrange anyrange', prosrc => 'range_cmp' },
+{ oid => '8849', descr => 'sort support',
+  proname => 'range_sortsupport', prorettype => 'void',
+  proargtypes => 'internal', prosrc => 'range_sortsupport' },
 { oid => '3871',
   proname => 'range_lt', prorettype => 'bool',
   proargtypes => 'anyrange anyrange', prosrc => 'range_lt' },