]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Add cross-type comparisons to contrib/btree_gin.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 3 Jul 2025 20:24:31 +0000 (16:24 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 3 Jul 2025 20:24:31 +0000 (16:24 -0400)
Extend the infrastructure in btree_gin.c to permit cross-type
operators, and add the code to support them for the int2, int4,
and int8 opclasses.  (To keep this patch digestible, I left
the other datatypes for a separate patch.)  This improves the
usability of btree_gin indexes by allowing them to support the
same set of queries that a regular btree index does.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Arseniy Mukhin <arseniy.mukhin.dev@gmail.com>
Discussion: https://postgr.es/m/262624.1738460652@sss.pgh.pa.us

13 files changed:
contrib/btree_gin/Makefile
contrib/btree_gin/btree_gin--1.3--1.4.sql [new file with mode: 0644]
contrib/btree_gin/btree_gin.c
contrib/btree_gin/btree_gin.control
contrib/btree_gin/expected/int2.out
contrib/btree_gin/expected/int4.out
contrib/btree_gin/expected/int8.out
contrib/btree_gin/meson.build
contrib/btree_gin/sql/int2.sql
contrib/btree_gin/sql/int4.sql
contrib/btree_gin/sql/int8.sql
doc/src/sgml/gin.sgml
src/tools/pgindent/typedefs.list

index 0a1581151681944ab5f3f5ea545e10ca1e7b9cdc..ad054598db6c93901dafdc2bf16dda1246f86ad8 100644 (file)
@@ -7,7 +7,7 @@ OBJS = \
 
 EXTENSION = btree_gin
 DATA = btree_gin--1.0.sql btree_gin--1.0--1.1.sql btree_gin--1.1--1.2.sql \
-        btree_gin--1.2--1.3.sql
+        btree_gin--1.2--1.3.sql btree_gin--1.3--1.4.sql
 PGFILEDESC = "btree_gin - B-tree equivalent GIN operator classes"
 
 REGRESS = install_btree_gin int2 int4 int8 float4 float8 money oid \
diff --git a/contrib/btree_gin/btree_gin--1.3--1.4.sql b/contrib/btree_gin/btree_gin--1.3--1.4.sql
new file mode 100644 (file)
index 0000000..4c77138
--- /dev/null
@@ -0,0 +1,63 @@
+/* contrib/btree_gin/btree_gin--1.3--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "ALTER EXTENSION btree_gin UPDATE TO '1.4'" to load this file. \quit
+
+--
+-- Cross-type operator support is new in 1.4.  We only need to worry
+-- about this for cross-type operators that exist in core.
+--
+-- Because the opclass extractQuery and consistent methods don't directly
+-- get any information about the datatype of the RHS value, we have to
+-- encode that in the operator strategy numbers.  The strategy numbers
+-- are the operator's normal btree strategy (1-5) plus 16 times a code
+-- for the RHS datatype.
+--
+
+ALTER OPERATOR FAMILY int2_ops USING gin
+ADD
+    -- Code 1: RHS is int4
+    OPERATOR        0x11    < (int2, int4),
+    OPERATOR        0x12    <= (int2, int4),
+    OPERATOR        0x13    = (int2, int4),
+    OPERATOR        0x14    >= (int2, int4),
+    OPERATOR        0x15    > (int2, int4),
+    -- Code 2: RHS is int8
+    OPERATOR        0x21    < (int2, int8),
+    OPERATOR        0x22    <= (int2, int8),
+    OPERATOR        0x23    = (int2, int8),
+    OPERATOR        0x24    >= (int2, int8),
+    OPERATOR        0x25    > (int2, int8)
+;
+
+ALTER OPERATOR FAMILY int4_ops USING gin
+ADD
+    -- Code 1: RHS is int2
+    OPERATOR        0x11    < (int4, int2),
+    OPERATOR        0x12    <= (int4, int2),
+    OPERATOR        0x13    = (int4, int2),
+    OPERATOR        0x14    >= (int4, int2),
+    OPERATOR        0x15    > (int4, int2),
+    -- Code 2: RHS is int8
+    OPERATOR        0x21    < (int4, int8),
+    OPERATOR        0x22    <= (int4, int8),
+    OPERATOR        0x23    = (int4, int8),
+    OPERATOR        0x24    >= (int4, int8),
+    OPERATOR        0x25    > (int4, int8)
+;
+
+ALTER OPERATOR FAMILY int8_ops USING gin
+ADD
+    -- Code 1: RHS is int2
+    OPERATOR        0x11    < (int8, int2),
+    OPERATOR        0x12    <= (int8, int2),
+    OPERATOR        0x13    = (int8, int2),
+    OPERATOR        0x14    >= (int8, int2),
+    OPERATOR        0x15    > (int8, int2),
+    -- Code 2: RHS is int4
+    OPERATOR        0x21    < (int8, int4),
+    OPERATOR        0x22    <= (int8, int4),
+    OPERATOR        0x23    = (int8, int4),
+    OPERATOR        0x24    >= (int8, int4),
+    OPERATOR        0x25    > (int8, int4)
+;
index 98663cb86117edb0193ab6394c29a5d147e27715..818a33af97ff2550677b0b9dbc42fbb7517a48bb 100644 (file)
@@ -19,14 +19,29 @@ PG_MODULE_MAGIC_EXT(
                                        .version = PG_VERSION
 );
 
+/*
+ * Our opclasses use the same strategy numbers as btree (1-5) for same-type
+ * comparison operators.  For cross-type comparison operators, the
+ * low 4 bits of our strategy numbers are the btree strategy number,
+ * and the upper bits are a code for the right-hand-side data type.
+ */
+#define BTGIN_GET_BTREE_STRATEGY(strat)                ((strat) & 0x0F)
+#define BTGIN_GET_RHS_TYPE_CODE(strat)         ((strat) >> 4)
+
+/* extra data passed from gin_btree_extract_query to gin_btree_compare_prefix */
 typedef struct QueryInfo
 {
-       StrategyNumber strategy;
-       Datum           datum;
-       bool            is_varlena;
-       Datum           (*typecmp) (FunctionCallInfo);
+       StrategyNumber strategy;        /* operator strategy number */
+       Datum           orig_datum;             /* original query (comparison) datum */
+       Datum           entry_datum;    /* datum we reported as the entry value */
+       PGFunction      typecmp;                /* appropriate btree comparison function */
 } QueryInfo;
 
+typedef Datum (*btree_gin_convert_function) (Datum input);
+
+typedef Datum (*btree_gin_leftmost_function) (void);
+
+
 /*** GIN support functions shared by all datatypes ***/
 
 static Datum
@@ -36,6 +51,7 @@ gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
        int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
        Datum      *entries = (Datum *) palloc(sizeof(Datum));
 
+       /* Ensure that values stored in the index are not toasted */
        if (is_varlena)
                datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
        entries[0] = datum;
@@ -44,19 +60,12 @@ gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
        PG_RETURN_POINTER(entries);
 }
 
-/*
- * For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
- * BTEqualStrategyNumber we want to start the index scan at the
- * supplied query datum, and work forward. For BTLessStrategyNumber
- * and BTLessEqualStrategyNumber, we need to start at the leftmost
- * key, and work forward until the supplied query datum (which must be
- * sent along inside the QueryInfo structure).
- */
 static Datum
 gin_btree_extract_query(FunctionCallInfo fcinfo,
-                                               bool is_varlena,
-                                               Datum (*leftmostvalue) (void),
-                                               Datum (*typecmp) (FunctionCallInfo))
+                                               btree_gin_leftmost_function leftmostvalue,
+                                               const bool *rhs_is_varlena,
+                                               const btree_gin_convert_function *cvt_fns,
+                                               const PGFunction *cmp_fns)
 {
        Datum           datum = PG_GETARG_DATUM(0);
        int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
@@ -65,21 +74,40 @@ gin_btree_extract_query(FunctionCallInfo fcinfo,
        Pointer   **extra_data = (Pointer **) PG_GETARG_POINTER(4);
        Datum      *entries = (Datum *) palloc(sizeof(Datum));
        QueryInfo  *data = (QueryInfo *) palloc(sizeof(QueryInfo));
-       bool       *ptr_partialmatch;
+       bool       *ptr_partialmatch = (bool *) palloc(sizeof(bool));
+       int                     btree_strat,
+                               rhs_code;
 
+       /*
+        * Extract the btree strategy code and the RHS data type code from the
+        * given strategy number.
+        */
+       btree_strat = BTGIN_GET_BTREE_STRATEGY(strategy);
+       rhs_code = BTGIN_GET_RHS_TYPE_CODE(strategy);
+
+       /*
+        * Detoast the comparison datum.  This isn't necessary for correctness,
+        * but it can save repeat detoastings within the comparison function.
+        */
+       if (rhs_is_varlena[rhs_code])
+               datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
+
+       /* Prep single comparison key with possible partial-match flag */
        *nentries = 1;
-       ptr_partialmatch = *partialmatch = (bool *) palloc(sizeof(bool));
+       *partialmatch = ptr_partialmatch;
        *ptr_partialmatch = false;
-       if (is_varlena)
-               datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
-       data->strategy = strategy;
-       data->datum = datum;
-       data->is_varlena = is_varlena;
-       data->typecmp = typecmp;
-       *extra_data = (Pointer *) palloc(sizeof(Pointer));
-       **extra_data = (Pointer) data;
 
-       switch (strategy)
+       /*
+        * For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
+        * BTEqualStrategyNumber we want to start the index scan at the supplied
+        * query datum, and work forward.  For BTLessStrategyNumber and
+        * BTLessEqualStrategyNumber, we need to start at the leftmost key, and
+        * work forward until the supplied query datum (which we'll send along
+        * inside the QueryInfo structure).  Use partial match rules except for
+        * BTEqualStrategyNumber without a conversion function.  (If there is a
+        * conversion function, comparison to the entry value is not trustworthy.)
+        */
+       switch (btree_strat)
        {
                case BTLessStrategyNumber:
                case BTLessEqualStrategyNumber:
@@ -91,75 +119,106 @@ gin_btree_extract_query(FunctionCallInfo fcinfo,
                        *ptr_partialmatch = true;
                        /* FALLTHROUGH */
                case BTEqualStrategyNumber:
-                       entries[0] = datum;
+                       /* If we have a conversion function, apply it */
+                       if (cvt_fns && cvt_fns[rhs_code])
+                       {
+                               entries[0] = (*cvt_fns[rhs_code]) (datum);
+                               *ptr_partialmatch = true;
+                       }
+                       else
+                               entries[0] = datum;
                        break;
                default:
                        elog(ERROR, "unrecognized strategy number: %d", strategy);
        }
 
+       /* Fill "extra" data */
+       data->strategy = strategy;
+       data->orig_datum = datum;
+       data->entry_datum = entries[0];
+       data->typecmp = cmp_fns[rhs_code];
+       *extra_data = (Pointer *) palloc(sizeof(Pointer));
+       **extra_data = (Pointer) data;
+
        PG_RETURN_POINTER(entries);
 }
 
-/*
- * Datum a is a value from extract_query method and for BTLess*
- * strategy it is a left-most value.  So, use original datum from QueryInfo
- * to decide to stop scanning or not.  Datum b is always from index.
- */
 static Datum
 gin_btree_compare_prefix(FunctionCallInfo fcinfo)
 {
-       Datum           a = PG_GETARG_DATUM(0);
-       Datum           b = PG_GETARG_DATUM(1);
+       Datum           partial_key PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(0);
+       Datum           key = PG_GETARG_DATUM(1);
        QueryInfo  *data = (QueryInfo *) PG_GETARG_POINTER(3);
        int32           res,
                                cmp;
 
+       /*
+        * partial_key is only an approximation to the real comparison value,
+        * especially if it's a leftmost value.  We can get an accurate answer by
+        * doing a possibly-cross-type comparison to the real comparison value.
+        * (Note that partial_key and key are of the indexed datatype while
+        * orig_datum is of the query operator's RHS datatype.)
+        *
+        * But just to be sure that things are what we expect, let's assert that
+        * partial_key is indeed what gin_btree_extract_query reported, so that
+        * we'll notice if anyone ever changes the core code in a way that breaks
+        * our assumptions.
+        */
+       Assert(partial_key == data->entry_datum);
+
        cmp = DatumGetInt32(CallerFInfoFunctionCall2(data->typecmp,
                                                                                                 fcinfo->flinfo,
                                                                                                 PG_GET_COLLATION(),
-                                                                                                (data->strategy == BTLessStrategyNumber ||
-                                                                                                 data->strategy == BTLessEqualStrategyNumber)
-                                                                                                ? data->datum : a,
-                                                                                                b));
+                                                                                                data->orig_datum,
+                                                                                                key));
 
-       switch (data->strategy)
+       /*
+        * Convert the comparison result to the correct thing for the search
+        * operator strategy.  When dealing with cross-type comparisons, an
+        * imprecise entry datum could lead GIN to start the scan just before the
+        * first possible match, so we must continue the scan if the current index
+        * entry doesn't satisfy the search condition for >= and > cases.  But if
+        * that happens in an = search we can stop, because an imprecise entry
+        * datum means that the search value is unrepresentable in the indexed
+        * data type, so that there will be no exact matches.
+        */
+       switch (BTGIN_GET_BTREE_STRATEGY(data->strategy))
        {
                case BTLessStrategyNumber:
                        /* If original datum > indexed one then return match */
                        if (cmp > 0)
                                res = 0;
                        else
-                               res = 1;
+                               res = 1;                /* end scan */
                        break;
                case BTLessEqualStrategyNumber:
-                       /* The same except equality */
+                       /* If original datum >= indexed one then return match */
                        if (cmp >= 0)
                                res = 0;
                        else
-                               res = 1;
+                               res = 1;                /* end scan */
                        break;
                case BTEqualStrategyNumber:
-                       if (cmp != 0)
-                               res = 1;
-                       else
+                       /* If original datum = indexed one then return match */
+                       /* See above about why we can end scan when cmp < 0 */
+                       if (cmp == 0)
                                res = 0;
+                       else
+                               res = 1;                /* end scan */
                        break;
                case BTGreaterEqualStrategyNumber:
                        /* If original datum <= indexed one then return match */
                        if (cmp <= 0)
                                res = 0;
                        else
-                               res = 1;
+                               res = -1;               /* keep scanning */
                        break;
                case BTGreaterStrategyNumber:
-                       /* If original datum <= indexed one then return match */
-                       /* If original datum == indexed one then continue scan */
+                       /* If original datum < indexed one then return match */
                        if (cmp < 0)
                                res = 0;
-                       else if (cmp == 0)
-                               res = -1;
                        else
-                               res = 1;
+                               res = -1;               /* keep scanning */
                        break;
                default:
                        elog(ERROR, "unrecognized strategy number: %d",
@@ -182,19 +241,20 @@ gin_btree_consistent(PG_FUNCTION_ARGS)
 
 /*** GIN_SUPPORT macro defines the datatype specific functions ***/
 
-#define GIN_SUPPORT(type, is_varlena, leftmostvalue, typecmp)                          \
+#define GIN_SUPPORT(type, leftmostvalue, is_varlena, cvtfns, cmpfns)           \
 PG_FUNCTION_INFO_V1(gin_extract_value_##type);                                                         \
 Datum                                                                                                                                          \
 gin_extract_value_##type(PG_FUNCTION_ARGS)                                                                     \
 {                                                                                                                                                      \
-       return gin_btree_extract_value(fcinfo, is_varlena);                                             \
+       return gin_btree_extract_value(fcinfo, is_varlena[0]);                                  \
 }      \
 PG_FUNCTION_INFO_V1(gin_extract_query_##type);                                                         \
 Datum                                                                                                                                          \
 gin_extract_query_##type(PG_FUNCTION_ARGS)                                                                     \
 {                                                                                                                                                      \
        return gin_btree_extract_query(fcinfo,                                                                  \
-                                                                  is_varlena, leftmostvalue, typecmp);         \
+                                                                  leftmostvalue, is_varlena,                           \
+                                                                  cvtfns, cmpfns);                                                     \
 }      \
 PG_FUNCTION_INFO_V1(gin_compare_prefix_##type);                                                                \
 Datum                                                                                                                                          \
@@ -206,13 +266,66 @@ gin_compare_prefix_##type(PG_FUNCTION_ARGS)                                                                       \
 
 /*** Datatype specifications ***/
 
+/* Function to produce the least possible value of the indexed datatype */
 static Datum
 leftmostvalue_int2(void)
 {
        return Int16GetDatum(SHRT_MIN);
 }
 
-GIN_SUPPORT(int2, false, leftmostvalue_int2, btint2cmp)
+/*
+ * For cross-type support, we must provide conversion functions that produce
+ * a Datum of the indexed datatype, since GIN requires the "entry" datums to
+ * be of that type.  If an exact conversion is not possible, produce a value
+ * that will lead GIN to find the first index entry that is greater than
+ * or equal to the actual comparison value.  (But rounding down is OK, so
+ * sometimes we might find an index entry that's just less than the
+ * comparison value.)
+ *
+ * For integer values, it's sufficient to clamp the input to be in-range.
+ *
+ * Note: for out-of-range input values, we could in theory detect that the
+ * search condition matches all or none of the index, and avoid a useless
+ * index descent in the latter case.  Such searches are probably rare though,
+ * so we don't contort this code enough to do that.
+ */
+static Datum
+cvt_int4_int2(Datum input)
+{
+       int32           val = DatumGetInt32(input);
+
+       val = Max(val, SHRT_MIN);
+       val = Min(val, SHRT_MAX);
+       return Int16GetDatum((int16) val);
+}
+
+static Datum
+cvt_int8_int2(Datum input)
+{
+       int64           val = DatumGetInt64(input);
+
+       val = Max(val, SHRT_MIN);
+       val = Min(val, SHRT_MAX);
+       return Int16GetDatum((int16) val);
+}
+
+/*
+ * RHS-type-is-varlena flags, conversion and comparison function arrays,
+ * indexed by high bits of the operator strategy number.  A NULL in the
+ * conversion function array indicates that no conversion is needed, which
+ * will always be the case for the zero'th entry.  Note that the cross-type
+ * comparison functions should be the ones with the indexed datatype second.
+ */
+static const bool int2_rhs_is_varlena[] =
+{false, false, false};
+
+static const btree_gin_convert_function int2_cvt_fns[] =
+{NULL, cvt_int4_int2, cvt_int8_int2};
+
+static const PGFunction int2_cmp_fns[] =
+{btint2cmp, btint42cmp, btint82cmp};
+
+GIN_SUPPORT(int2, leftmostvalue_int2, int2_rhs_is_varlena, int2_cvt_fns, int2_cmp_fns)
 
 static Datum
 leftmostvalue_int4(void)
@@ -220,7 +333,34 @@ leftmostvalue_int4(void)
        return Int32GetDatum(INT_MIN);
 }
 
-GIN_SUPPORT(int4, false, leftmostvalue_int4, btint4cmp)
+static Datum
+cvt_int2_int4(Datum input)
+{
+       int16           val = DatumGetInt16(input);
+
+       return Int32GetDatum((int32) val);
+}
+
+static Datum
+cvt_int8_int4(Datum input)
+{
+       int64           val = DatumGetInt64(input);
+
+       val = Max(val, INT_MIN);
+       val = Min(val, INT_MAX);
+       return Int32GetDatum((int32) val);
+}
+
+static const bool int4_rhs_is_varlena[] =
+{false, false, false};
+
+static const btree_gin_convert_function int4_cvt_fns[] =
+{NULL, cvt_int2_int4, cvt_int8_int4};
+
+static const PGFunction int4_cmp_fns[] =
+{btint4cmp, btint24cmp, btint84cmp};
+
+GIN_SUPPORT(int4, leftmostvalue_int4, int4_rhs_is_varlena, int4_cvt_fns, int4_cmp_fns)
 
 static Datum
 leftmostvalue_int8(void)
@@ -228,7 +368,32 @@ leftmostvalue_int8(void)
        return Int64GetDatum(PG_INT64_MIN);
 }
 
-GIN_SUPPORT(int8, false, leftmostvalue_int8, btint8cmp)
+static Datum
+cvt_int2_int8(Datum input)
+{
+       int16           val = DatumGetInt16(input);
+
+       return Int64GetDatum((int64) val);
+}
+
+static Datum
+cvt_int4_int8(Datum input)
+{
+       int32           val = DatumGetInt32(input);
+
+       return Int64GetDatum((int64) val);
+}
+
+static const bool int8_rhs_is_varlena[] =
+{false, false, false};
+
+static const btree_gin_convert_function int8_cvt_fns[] =
+{NULL, cvt_int2_int8, cvt_int4_int8};
+
+static const PGFunction int8_cmp_fns[] =
+{btint8cmp, btint28cmp, btint48cmp};
+
+GIN_SUPPORT(int8, leftmostvalue_int8, int8_rhs_is_varlena, int8_cvt_fns, int8_cmp_fns)
 
 static Datum
 leftmostvalue_float4(void)
@@ -236,7 +401,13 @@ leftmostvalue_float4(void)
        return Float4GetDatum(-get_float4_infinity());
 }
 
-GIN_SUPPORT(float4, false, leftmostvalue_float4, btfloat4cmp)
+static const bool float4_rhs_is_varlena[] =
+{false};
+
+static const PGFunction float4_cmp_fns[] =
+{btfloat4cmp};
+
+GIN_SUPPORT(float4, leftmostvalue_float4, float4_rhs_is_varlena, NULL, float4_cmp_fns)
 
 static Datum
 leftmostvalue_float8(void)
@@ -244,7 +415,13 @@ leftmostvalue_float8(void)
        return Float8GetDatum(-get_float8_infinity());
 }
 
-GIN_SUPPORT(float8, false, leftmostvalue_float8, btfloat8cmp)
+static const bool float8_rhs_is_varlena[] =
+{false};
+
+static const PGFunction float8_cmp_fns[] =
+{btfloat8cmp};
+
+GIN_SUPPORT(float8, leftmostvalue_float8, float8_rhs_is_varlena, NULL, float8_cmp_fns)
 
 static Datum
 leftmostvalue_money(void)
@@ -252,7 +429,13 @@ leftmostvalue_money(void)
        return Int64GetDatum(PG_INT64_MIN);
 }
 
-GIN_SUPPORT(money, false, leftmostvalue_money, cash_cmp)
+static const bool money_rhs_is_varlena[] =
+{false};
+
+static const PGFunction money_cmp_fns[] =
+{cash_cmp};
+
+GIN_SUPPORT(money, leftmostvalue_money, money_rhs_is_varlena, NULL, money_cmp_fns)
 
 static Datum
 leftmostvalue_oid(void)
@@ -260,7 +443,13 @@ leftmostvalue_oid(void)
        return ObjectIdGetDatum(0);
 }
 
-GIN_SUPPORT(oid, false, leftmostvalue_oid, btoidcmp)
+static const bool oid_rhs_is_varlena[] =
+{false};
+
+static const PGFunction oid_cmp_fns[] =
+{btoidcmp};
+
+GIN_SUPPORT(oid, leftmostvalue_oid, oid_rhs_is_varlena, NULL, oid_cmp_fns)
 
 static Datum
 leftmostvalue_timestamp(void)
@@ -268,9 +457,21 @@ leftmostvalue_timestamp(void)
        return TimestampGetDatum(DT_NOBEGIN);
 }
 
-GIN_SUPPORT(timestamp, false, leftmostvalue_timestamp, timestamp_cmp)
+static const bool timestamp_rhs_is_varlena[] =
+{false};
+
+static const PGFunction timestamp_cmp_fns[] =
+{timestamp_cmp};
 
-GIN_SUPPORT(timestamptz, false, leftmostvalue_timestamp, timestamp_cmp)
+GIN_SUPPORT(timestamp, leftmostvalue_timestamp, timestamp_rhs_is_varlena, NULL, timestamp_cmp_fns)
+
+static const bool timestamptz_rhs_is_varlena[] =
+{false};
+
+static const PGFunction timestamptz_cmp_fns[] =
+{timestamp_cmp};
+
+GIN_SUPPORT(timestamptz, leftmostvalue_timestamp, timestamptz_rhs_is_varlena, NULL, timestamptz_cmp_fns)
 
 static Datum
 leftmostvalue_time(void)
@@ -278,7 +479,13 @@ leftmostvalue_time(void)
        return TimeADTGetDatum(0);
 }
 
-GIN_SUPPORT(time, false, leftmostvalue_time, time_cmp)
+static const bool time_rhs_is_varlena[] =
+{false};
+
+static const PGFunction time_cmp_fns[] =
+{time_cmp};
+
+GIN_SUPPORT(time, leftmostvalue_time, time_rhs_is_varlena, NULL, time_cmp_fns)
 
 static Datum
 leftmostvalue_timetz(void)
@@ -291,7 +498,13 @@ leftmostvalue_timetz(void)
        return TimeTzADTPGetDatum(v);
 }
 
-GIN_SUPPORT(timetz, false, leftmostvalue_timetz, timetz_cmp)
+static const bool timetz_rhs_is_varlena[] =
+{false};
+
+static const PGFunction timetz_cmp_fns[] =
+{timetz_cmp};
+
+GIN_SUPPORT(timetz, leftmostvalue_timetz, timetz_rhs_is_varlena, NULL, timetz_cmp_fns)
 
 static Datum
 leftmostvalue_date(void)
@@ -299,7 +512,13 @@ leftmostvalue_date(void)
        return DateADTGetDatum(DATEVAL_NOBEGIN);
 }
 
-GIN_SUPPORT(date, false, leftmostvalue_date, date_cmp)
+static const bool date_rhs_is_varlena[] =
+{false};
+
+static const PGFunction date_cmp_fns[] =
+{date_cmp};
+
+GIN_SUPPORT(date, leftmostvalue_date, date_rhs_is_varlena, NULL, date_cmp_fns)
 
 static Datum
 leftmostvalue_interval(void)
@@ -311,7 +530,13 @@ leftmostvalue_interval(void)
        return IntervalPGetDatum(v);
 }
 
-GIN_SUPPORT(interval, false, leftmostvalue_interval, interval_cmp)
+static const bool interval_rhs_is_varlena[] =
+{false};
+
+static const PGFunction interval_cmp_fns[] =
+{interval_cmp};
+
+GIN_SUPPORT(interval, leftmostvalue_interval, interval_rhs_is_varlena, NULL, interval_cmp_fns)
 
 static Datum
 leftmostvalue_macaddr(void)
@@ -321,7 +546,13 @@ leftmostvalue_macaddr(void)
        return MacaddrPGetDatum(v);
 }
 
-GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp)
+static const bool macaddr_rhs_is_varlena[] =
+{false};
+
+static const PGFunction macaddr_cmp_fns[] =
+{macaddr_cmp};
+
+GIN_SUPPORT(macaddr, leftmostvalue_macaddr, macaddr_rhs_is_varlena, NULL, macaddr_cmp_fns)
 
 static Datum
 leftmostvalue_macaddr8(void)
@@ -331,7 +562,13 @@ leftmostvalue_macaddr8(void)
        return Macaddr8PGetDatum(v);
 }
 
-GIN_SUPPORT(macaddr8, false, leftmostvalue_macaddr8, macaddr8_cmp)
+static const bool macaddr8_rhs_is_varlena[] =
+{false};
+
+static const PGFunction macaddr8_cmp_fns[] =
+{macaddr8_cmp};
+
+GIN_SUPPORT(macaddr8, leftmostvalue_macaddr8, macaddr8_rhs_is_varlena, NULL, macaddr8_cmp_fns)
 
 static Datum
 leftmostvalue_inet(void)
@@ -339,9 +576,21 @@ leftmostvalue_inet(void)
        return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
 }
 
-GIN_SUPPORT(inet, true, leftmostvalue_inet, network_cmp)
+static const bool inet_rhs_is_varlena[] =
+{true};
+
+static const PGFunction inet_cmp_fns[] =
+{network_cmp};
+
+GIN_SUPPORT(inet, leftmostvalue_inet, inet_rhs_is_varlena, NULL, inet_cmp_fns)
 
-GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
+static const bool cidr_rhs_is_varlena[] =
+{true};
+
+static const PGFunction cidr_cmp_fns[] =
+{network_cmp};
+
+GIN_SUPPORT(cidr, leftmostvalue_inet, cidr_rhs_is_varlena, NULL, cidr_cmp_fns)
 
 static Datum
 leftmostvalue_text(void)
@@ -349,9 +598,21 @@ leftmostvalue_text(void)
        return PointerGetDatum(cstring_to_text_with_len("", 0));
 }
 
-GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
+static const bool text_rhs_is_varlena[] =
+{true};
+
+static const PGFunction text_cmp_fns[] =
+{bttextcmp};
+
+GIN_SUPPORT(text, leftmostvalue_text, text_rhs_is_varlena, NULL, text_cmp_fns)
 
-GIN_SUPPORT(bpchar, true, leftmostvalue_text, bpcharcmp)
+static const bool bpchar_rhs_is_varlena[] =
+{true};
+
+static const PGFunction bpchar_cmp_fns[] =
+{bpcharcmp};
+
+GIN_SUPPORT(bpchar, leftmostvalue_text, bpchar_rhs_is_varlena, NULL, bpchar_cmp_fns)
 
 static Datum
 leftmostvalue_char(void)
@@ -359,9 +620,21 @@ leftmostvalue_char(void)
        return CharGetDatum(0);
 }
 
-GIN_SUPPORT(char, false, leftmostvalue_char, btcharcmp)
+static const bool char_rhs_is_varlena[] =
+{false};
+
+static const PGFunction char_cmp_fns[] =
+{btcharcmp};
+
+GIN_SUPPORT(char, leftmostvalue_char, char_rhs_is_varlena, NULL, char_cmp_fns)
 
-GIN_SUPPORT(bytea, true, leftmostvalue_text, byteacmp)
+static const bool bytea_rhs_is_varlena[] =
+{true};
+
+static const PGFunction bytea_cmp_fns[] =
+{byteacmp};
+
+GIN_SUPPORT(bytea, leftmostvalue_text, bytea_rhs_is_varlena, NULL, bytea_cmp_fns)
 
 static Datum
 leftmostvalue_bit(void)
@@ -372,7 +645,13 @@ leftmostvalue_bit(void)
                                                           Int32GetDatum(-1));
 }
 
-GIN_SUPPORT(bit, true, leftmostvalue_bit, bitcmp)
+static const bool bit_rhs_is_varlena[] =
+{true};
+
+static const PGFunction bit_cmp_fns[] =
+{bitcmp};
+
+GIN_SUPPORT(bit, leftmostvalue_bit, bit_rhs_is_varlena, NULL, bit_cmp_fns)
 
 static Datum
 leftmostvalue_varbit(void)
@@ -383,7 +662,13 @@ leftmostvalue_varbit(void)
                                                           Int32GetDatum(-1));
 }
 
-GIN_SUPPORT(varbit, true, leftmostvalue_varbit, bitcmp)
+static const bool varbit_rhs_is_varlena[] =
+{true};
+
+static const PGFunction varbit_cmp_fns[] =
+{bitcmp};
+
+GIN_SUPPORT(varbit, leftmostvalue_varbit, varbit_rhs_is_varlena, NULL, varbit_cmp_fns)
 
 /*
  * Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
@@ -428,7 +713,13 @@ leftmostvalue_numeric(void)
        return PointerGetDatum(NULL);
 }
 
-GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp)
+static const bool numeric_rhs_is_varlena[] =
+{true};
+
+static const PGFunction numeric_cmp_fns[] =
+{gin_numeric_cmp};
+
+GIN_SUPPORT(numeric, leftmostvalue_numeric, numeric_rhs_is_varlena, NULL, numeric_cmp_fns)
 
 /*
  * Use a similar trick to that used for numeric for enums, since we don't
@@ -477,7 +768,13 @@ leftmostvalue_enum(void)
        return ObjectIdGetDatum(InvalidOid);
 }
 
-GIN_SUPPORT(anyenum, false, leftmostvalue_enum, gin_enum_cmp)
+static const bool enum_rhs_is_varlena[] =
+{false};
+
+static const PGFunction enum_cmp_fns[] =
+{gin_enum_cmp};
+
+GIN_SUPPORT(anyenum, leftmostvalue_enum, enum_rhs_is_varlena, NULL, enum_cmp_fns)
 
 static Datum
 leftmostvalue_uuid(void)
@@ -491,7 +788,13 @@ leftmostvalue_uuid(void)
        return UUIDPGetDatum(retval);
 }
 
-GIN_SUPPORT(uuid, false, leftmostvalue_uuid, uuid_cmp)
+static const bool uuid_rhs_is_varlena[] =
+{false};
+
+static const PGFunction uuid_cmp_fns[] =
+{uuid_cmp};
+
+GIN_SUPPORT(uuid, leftmostvalue_uuid, uuid_rhs_is_varlena, NULL, uuid_cmp_fns)
 
 static Datum
 leftmostvalue_name(void)
@@ -501,7 +804,13 @@ leftmostvalue_name(void)
        return NameGetDatum(result);
 }
 
-GIN_SUPPORT(name, false, leftmostvalue_name, btnamecmp)
+static const bool name_rhs_is_varlena[] =
+{false};
+
+static const PGFunction name_cmp_fns[] =
+{btnamecmp};
+
+GIN_SUPPORT(name, leftmostvalue_name, name_rhs_is_varlena, NULL, name_cmp_fns)
 
 static Datum
 leftmostvalue_bool(void)
@@ -509,4 +818,10 @@ leftmostvalue_bool(void)
        return BoolGetDatum(false);
 }
 
-GIN_SUPPORT(bool, false, leftmostvalue_bool, btboolcmp)
+static const bool bool_rhs_is_varlena[] =
+{false};
+
+static const PGFunction bool_cmp_fns[] =
+{btboolcmp};
+
+GIN_SUPPORT(bool, leftmostvalue_bool, bool_rhs_is_varlena, NULL, bool_cmp_fns)
index 67d0c997d8d26e4b5e23cb79249570f553015500..0c77c81727117a0ab0f013152e077151f25102ca 100644 (file)
@@ -1,6 +1,6 @@
 # btree_gin extension
 comment = 'support for indexing common datatypes in GIN'
-default_version = '1.3'
+default_version = '1.4'
 module_pathname = '$libdir/btree_gin'
 relocatable = true
 trusted = true
index 20d66a1b0554512367ea0279013fc9fc7833750b..bcfa68f671a25070426b9912bb1a0c8364ce71d2 100644 (file)
@@ -42,3 +42,193 @@ SELECT * FROM test_int2 WHERE i>1::int2 ORDER BY i;
  3
 (2 rows)
 
+explain (costs off)
+SELECT * FROM test_int2 WHERE i<1::int4 ORDER BY i;
+                QUERY PLAN                 
+-------------------------------------------
+ Sort
+   Sort Key: i
+   ->  Bitmap Heap Scan on test_int2
+         Recheck Cond: (i < 1)
+         ->  Bitmap Index Scan on idx_int2
+               Index Cond: (i < 1)
+(6 rows)
+
+SELECT * FROM test_int2 WHERE i<1::int4 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+(3 rows)
+
+SELECT * FROM test_int2 WHERE i<=1::int4 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+  1
+(4 rows)
+
+SELECT * FROM test_int2 WHERE i=1::int4 ORDER BY i;
+ i 
+---
+ 1
+(1 row)
+
+SELECT * FROM test_int2 WHERE i>=1::int4 ORDER BY i;
+ i 
+---
+ 1
+ 2
+ 3
+(3 rows)
+
+SELECT * FROM test_int2 WHERE i>1::int4 ORDER BY i;
+ i 
+---
+ 2
+ 3
+(2 rows)
+
+explain (costs off)
+SELECT * FROM test_int2 WHERE i<1::int8 ORDER BY i;
+                 QUERY PLAN                  
+---------------------------------------------
+ Sort
+   Sort Key: i
+   ->  Bitmap Heap Scan on test_int2
+         Recheck Cond: (i < '1'::bigint)
+         ->  Bitmap Index Scan on idx_int2
+               Index Cond: (i < '1'::bigint)
+(6 rows)
+
+SELECT * FROM test_int2 WHERE i<1::int8 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+(3 rows)
+
+SELECT * FROM test_int2 WHERE i<=1::int8 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+  1
+(4 rows)
+
+SELECT * FROM test_int2 WHERE i=1::int8 ORDER BY i;
+ i 
+---
+ 1
+(1 row)
+
+SELECT * FROM test_int2 WHERE i>=1::int8 ORDER BY i;
+ i 
+---
+ 1
+ 2
+ 3
+(3 rows)
+
+SELECT * FROM test_int2 WHERE i>1::int8 ORDER BY i;
+ i 
+---
+ 2
+ 3
+(2 rows)
+
+-- Check endpoint and out-of-range cases
+INSERT INTO test_int2 VALUES ((-32768)::int2),(32767);
+SELECT gin_clean_pending_list('idx_int2');
+ gin_clean_pending_list 
+------------------------
+                      1
+(1 row)
+
+SELECT * FROM test_int2 WHERE i<(-32769)::int4 ORDER BY i;
+ i 
+---
+(0 rows)
+
+SELECT * FROM test_int2 WHERE i<=(-32769)::int4 ORDER BY i;
+ i 
+---
+(0 rows)
+
+SELECT * FROM test_int2 WHERE i=(-32769)::int4 ORDER BY i;
+ i 
+---
+(0 rows)
+
+SELECT * FROM test_int2 WHERE i>=(-32769)::int4 ORDER BY i;
+   i    
+--------
+ -32768
+     -2
+     -1
+      0
+      1
+      2
+      3
+  32767
+(8 rows)
+
+SELECT * FROM test_int2 WHERE i>(-32769)::int4 ORDER BY i;
+   i    
+--------
+ -32768
+     -2
+     -1
+      0
+      1
+      2
+      3
+  32767
+(8 rows)
+
+SELECT * FROM test_int2 WHERE i<32768::int4 ORDER BY i;
+   i    
+--------
+ -32768
+     -2
+     -1
+      0
+      1
+      2
+      3
+  32767
+(8 rows)
+
+SELECT * FROM test_int2 WHERE i<=32768::int4 ORDER BY i;
+   i    
+--------
+ -32768
+     -2
+     -1
+      0
+      1
+      2
+      3
+  32767
+(8 rows)
+
+SELECT * FROM test_int2 WHERE i=32768::int4 ORDER BY i;
+ i 
+---
+(0 rows)
+
+SELECT * FROM test_int2 WHERE i>=32768::int4 ORDER BY i;
+ i 
+---
+(0 rows)
+
+SELECT * FROM test_int2 WHERE i>32768::int4 ORDER BY i;
+ i 
+---
+(0 rows)
+
index 0f0122c6f5e0373f92b09b6fdd4ceb2fe9fa4b30..e62791e18bdc26129f6dc2b7ab484458dc4fcda1 100644 (file)
@@ -42,3 +42,103 @@ SELECT * FROM test_int4 WHERE i>1::int4 ORDER BY i;
  3
 (2 rows)
 
+explain (costs off)
+SELECT * FROM test_int4 WHERE i<1::int2 ORDER BY i;
+                  QUERY PLAN                   
+-----------------------------------------------
+ Sort
+   Sort Key: i
+   ->  Bitmap Heap Scan on test_int4
+         Recheck Cond: (i < '1'::smallint)
+         ->  Bitmap Index Scan on idx_int4
+               Index Cond: (i < '1'::smallint)
+(6 rows)
+
+SELECT * FROM test_int4 WHERE i<1::int2 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+(3 rows)
+
+SELECT * FROM test_int4 WHERE i<=1::int2 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+  1
+(4 rows)
+
+SELECT * FROM test_int4 WHERE i=1::int2 ORDER BY i;
+ i 
+---
+ 1
+(1 row)
+
+SELECT * FROM test_int4 WHERE i>=1::int2 ORDER BY i;
+ i 
+---
+ 1
+ 2
+ 3
+(3 rows)
+
+SELECT * FROM test_int4 WHERE i>1::int2 ORDER BY i;
+ i 
+---
+ 2
+ 3
+(2 rows)
+
+explain (costs off)
+SELECT * FROM test_int4 WHERE i<1::int8 ORDER BY i;
+                 QUERY PLAN                  
+---------------------------------------------
+ Sort
+   Sort Key: i
+   ->  Bitmap Heap Scan on test_int4
+         Recheck Cond: (i < '1'::bigint)
+         ->  Bitmap Index Scan on idx_int4
+               Index Cond: (i < '1'::bigint)
+(6 rows)
+
+SELECT * FROM test_int4 WHERE i<1::int8 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+(3 rows)
+
+SELECT * FROM test_int4 WHERE i<=1::int8 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+  1
+(4 rows)
+
+SELECT * FROM test_int4 WHERE i=1::int8 ORDER BY i;
+ i 
+---
+ 1
+(1 row)
+
+SELECT * FROM test_int4 WHERE i>=1::int8 ORDER BY i;
+ i 
+---
+ 1
+ 2
+ 3
+(3 rows)
+
+SELECT * FROM test_int4 WHERE i>1::int8 ORDER BY i;
+ i 
+---
+ 2
+ 3
+(2 rows)
+
index 307e19e7a056de5e3038897ee3a1ae998cf37a46..c9aceb9d357c6313fb9a28571391156f8f97e51c 100644 (file)
@@ -42,3 +42,103 @@ SELECT * FROM test_int8 WHERE i>1::int8 ORDER BY i;
  3
 (2 rows)
 
+explain (costs off)
+SELECT * FROM test_int8 WHERE i<1::int2 ORDER BY i;
+                  QUERY PLAN                   
+-----------------------------------------------
+ Sort
+   Sort Key: i
+   ->  Bitmap Heap Scan on test_int8
+         Recheck Cond: (i < '1'::smallint)
+         ->  Bitmap Index Scan on idx_int8
+               Index Cond: (i < '1'::smallint)
+(6 rows)
+
+SELECT * FROM test_int8 WHERE i<1::int2 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+(3 rows)
+
+SELECT * FROM test_int8 WHERE i<=1::int2 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+  1
+(4 rows)
+
+SELECT * FROM test_int8 WHERE i=1::int2 ORDER BY i;
+ i 
+---
+ 1
+(1 row)
+
+SELECT * FROM test_int8 WHERE i>=1::int2 ORDER BY i;
+ i 
+---
+ 1
+ 2
+ 3
+(3 rows)
+
+SELECT * FROM test_int8 WHERE i>1::int2 ORDER BY i;
+ i 
+---
+ 2
+ 3
+(2 rows)
+
+explain (costs off)
+SELECT * FROM test_int8 WHERE i<1::int4 ORDER BY i;
+                QUERY PLAN                 
+-------------------------------------------
+ Sort
+   Sort Key: i
+   ->  Bitmap Heap Scan on test_int8
+         Recheck Cond: (i < 1)
+         ->  Bitmap Index Scan on idx_int8
+               Index Cond: (i < 1)
+(6 rows)
+
+SELECT * FROM test_int8 WHERE i<1::int4 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+(3 rows)
+
+SELECT * FROM test_int8 WHERE i<=1::int4 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+  1
+(4 rows)
+
+SELECT * FROM test_int8 WHERE i=1::int4 ORDER BY i;
+ i 
+---
+ 1
+(1 row)
+
+SELECT * FROM test_int8 WHERE i>=1::int4 ORDER BY i;
+ i 
+---
+ 1
+ 2
+ 3
+(3 rows)
+
+SELECT * FROM test_int8 WHERE i>1::int4 ORDER BY i;
+ i 
+---
+ 2
+ 3
+(2 rows)
+
index b2749f6e66951e9f8362a48b55c1cd36e4d0f95e..ece0a716973cedf94fd730c79db76ad11375063a 100644 (file)
@@ -22,6 +22,7 @@ install_data(
   'btree_gin--1.0--1.1.sql',
   'btree_gin--1.1--1.2.sql',
   'btree_gin--1.2--1.3.sql',
+  'btree_gin--1.3--1.4.sql',
   kwargs: contrib_data_args,
 )
 
index f06f11702f54e38eb4e467ca2ad717dcbf2248ba..959e0f6cfde0164f61e4273624b24508ac652857 100644 (file)
@@ -13,3 +13,38 @@ SELECT * FROM test_int2 WHERE i<=1::int2 ORDER BY i;
 SELECT * FROM test_int2 WHERE i=1::int2 ORDER BY i;
 SELECT * FROM test_int2 WHERE i>=1::int2 ORDER BY i;
 SELECT * FROM test_int2 WHERE i>1::int2 ORDER BY i;
+
+explain (costs off)
+SELECT * FROM test_int2 WHERE i<1::int4 ORDER BY i;
+
+SELECT * FROM test_int2 WHERE i<1::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i<=1::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i=1::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i>=1::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i>1::int4 ORDER BY i;
+
+explain (costs off)
+SELECT * FROM test_int2 WHERE i<1::int8 ORDER BY i;
+
+SELECT * FROM test_int2 WHERE i<1::int8 ORDER BY i;
+SELECT * FROM test_int2 WHERE i<=1::int8 ORDER BY i;
+SELECT * FROM test_int2 WHERE i=1::int8 ORDER BY i;
+SELECT * FROM test_int2 WHERE i>=1::int8 ORDER BY i;
+SELECT * FROM test_int2 WHERE i>1::int8 ORDER BY i;
+
+-- Check endpoint and out-of-range cases
+
+INSERT INTO test_int2 VALUES ((-32768)::int2),(32767);
+SELECT gin_clean_pending_list('idx_int2');
+
+SELECT * FROM test_int2 WHERE i<(-32769)::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i<=(-32769)::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i=(-32769)::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i>=(-32769)::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i>(-32769)::int4 ORDER BY i;
+
+SELECT * FROM test_int2 WHERE i<32768::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i<=32768::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i=32768::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i>=32768::int4 ORDER BY i;
+SELECT * FROM test_int2 WHERE i>32768::int4 ORDER BY i;
index 6499c296307224896a9b85aa0c3a76a1980decfd..9a45530b63ad79ad4f82f4c5e3b6acfd3d40eb9f 100644 (file)
@@ -13,3 +13,21 @@ SELECT * FROM test_int4 WHERE i<=1::int4 ORDER BY i;
 SELECT * FROM test_int4 WHERE i=1::int4 ORDER BY i;
 SELECT * FROM test_int4 WHERE i>=1::int4 ORDER BY i;
 SELECT * FROM test_int4 WHERE i>1::int4 ORDER BY i;
+
+explain (costs off)
+SELECT * FROM test_int4 WHERE i<1::int2 ORDER BY i;
+
+SELECT * FROM test_int4 WHERE i<1::int2 ORDER BY i;
+SELECT * FROM test_int4 WHERE i<=1::int2 ORDER BY i;
+SELECT * FROM test_int4 WHERE i=1::int2 ORDER BY i;
+SELECT * FROM test_int4 WHERE i>=1::int2 ORDER BY i;
+SELECT * FROM test_int4 WHERE i>1::int2 ORDER BY i;
+
+explain (costs off)
+SELECT * FROM test_int4 WHERE i<1::int8 ORDER BY i;
+
+SELECT * FROM test_int4 WHERE i<1::int8 ORDER BY i;
+SELECT * FROM test_int4 WHERE i<=1::int8 ORDER BY i;
+SELECT * FROM test_int4 WHERE i=1::int8 ORDER BY i;
+SELECT * FROM test_int4 WHERE i>=1::int8 ORDER BY i;
+SELECT * FROM test_int4 WHERE i>1::int8 ORDER BY i;
index 4d9c2871814c4e7576bc2adcebad70da5fd6ebf6..b31f27c69b90a1cd09cb7b0ecbb37380e2cccc74 100644 (file)
@@ -13,3 +13,21 @@ SELECT * FROM test_int8 WHERE i<=1::int8 ORDER BY i;
 SELECT * FROM test_int8 WHERE i=1::int8 ORDER BY i;
 SELECT * FROM test_int8 WHERE i>=1::int8 ORDER BY i;
 SELECT * FROM test_int8 WHERE i>1::int8 ORDER BY i;
+
+explain (costs off)
+SELECT * FROM test_int8 WHERE i<1::int2 ORDER BY i;
+
+SELECT * FROM test_int8 WHERE i<1::int2 ORDER BY i;
+SELECT * FROM test_int8 WHERE i<=1::int2 ORDER BY i;
+SELECT * FROM test_int8 WHERE i=1::int2 ORDER BY i;
+SELECT * FROM test_int8 WHERE i>=1::int2 ORDER BY i;
+SELECT * FROM test_int8 WHERE i>1::int2 ORDER BY i;
+
+explain (costs off)
+SELECT * FROM test_int8 WHERE i<1::int4 ORDER BY i;
+
+SELECT * FROM test_int8 WHERE i<1::int4 ORDER BY i;
+SELECT * FROM test_int8 WHERE i<=1::int4 ORDER BY i;
+SELECT * FROM test_int8 WHERE i=1::int4 ORDER BY i;
+SELECT * FROM test_int8 WHERE i>=1::int4 ORDER BY i;
+SELECT * FROM test_int8 WHERE i>1::int4 ORDER BY i;
index 46e87e01324dd00c460dd98746788bd95d5ccfa3..82410b1fbdfa16ea4211b4d2b1d625d6f053250f 100644 (file)
                               Pointer extra_data)</function></term>
      <listitem>
       <para>
-       Compare a partial-match query key to an index key.  Returns an integer
+       Compare a partial-match query key to an index key.
+       <literal>partial_key</literal> is a query key that was returned
+       by <function>extractQuery</function> with an indication that it
+       requires partial match, and <literal>key</literal> is an index entry.
+       Returns an integer
        whose sign indicates the result: less than zero means the index key
        does not match the query, but the index scan should continue; zero
        means that the index key does match the query; greater than zero
index 7544e7c5073dbe216650bb538b667b7fc5a7e15e..114bdafafdfa80568317f3dca774251ae6b317c1 100644 (file)
@@ -3481,6 +3481,8 @@ bloom_filter
 boolKEY
 brin_column_state
 brin_serialize_callback_type
+btree_gin_convert_function
+btree_gin_leftmost_function
 bytea
 cached_re_str
 canonicalize_state