LCOV - code coverage report
Current view: top level - contrib/btree_gin - btree_gin.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 253 260 97.3 %
Date: 2025-07-05 17:18:26 Functions: 218 218 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * contrib/btree_gin/btree_gin.c
       3             :  */
       4             : #include "postgres.h"
       5             : 
       6             : #include <limits.h>
       7             : 
       8             : #include "access/stratnum.h"
       9             : #include "mb/pg_wchar.h"
      10             : #include "utils/builtins.h"
      11             : #include "utils/date.h"
      12             : #include "utils/float.h"
      13             : #include "utils/inet.h"
      14             : #include "utils/numeric.h"
      15             : #include "utils/timestamp.h"
      16             : #include "utils/uuid.h"
      17             : #include "varatt.h"
      18             : 
      19          60 : PG_MODULE_MAGIC_EXT(
      20             :                     .name = "btree_gin",
      21             :                     .version = PG_VERSION
      22             : );
      23             : 
      24             : /*
      25             :  * Our opclasses use the same strategy numbers as btree (1-5) for same-type
      26             :  * comparison operators.  For cross-type comparison operators, the
      27             :  * low 4 bits of our strategy numbers are the btree strategy number,
      28             :  * and the upper bits are a code for the right-hand-side data type.
      29             :  */
      30             : #define BTGIN_GET_BTREE_STRATEGY(strat)     ((strat) & 0x0F)
      31             : #define BTGIN_GET_RHS_TYPE_CODE(strat)      ((strat) >> 4)
      32             : 
      33             : /* extra data passed from gin_btree_extract_query to gin_btree_compare_prefix */
      34             : typedef struct QueryInfo
      35             : {
      36             :     StrategyNumber strategy;    /* operator strategy number */
      37             :     Datum       orig_datum;     /* original query (comparison) datum */
      38             :     Datum       entry_datum;    /* datum we reported as the entry value */
      39             :     PGFunction  typecmp;        /* appropriate btree comparison function */
      40             : } QueryInfo;
      41             : 
      42             : typedef Datum (*btree_gin_convert_function) (Datum input);
      43             : 
      44             : typedef Datum (*btree_gin_leftmost_function) (void);
      45             : 
      46             : 
      47             : /*** GIN support functions shared by all datatypes ***/
      48             : 
      49             : static Datum
      50      200370 : gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
      51             : {
      52      200370 :     Datum       datum = PG_GETARG_DATUM(0);
      53      200370 :     int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
      54      200370 :     Datum      *entries = (Datum *) palloc(sizeof(Datum));
      55             : 
      56             :     /* Ensure that values stored in the index are not toasted */
      57      200370 :     if (is_varlena)
      58         112 :         datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
      59      200370 :     entries[0] = datum;
      60      200370 :     *nentries = 1;
      61             : 
      62      200370 :     PG_RETURN_POINTER(entries);
      63             : }
      64             : 
      65             : static Datum
      66        1372 : gin_btree_extract_query(FunctionCallInfo fcinfo,
      67             :                         btree_gin_leftmost_function leftmostvalue,
      68             :                         const bool *rhs_is_varlena,
      69             :                         const btree_gin_convert_function *cvt_fns,
      70             :                         const PGFunction *cmp_fns)
      71             : {
      72        1372 :     Datum       datum = PG_GETARG_DATUM(0);
      73        1372 :     int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
      74        1372 :     StrategyNumber strategy = PG_GETARG_UINT16(2);
      75        1372 :     bool      **partialmatch = (bool **) PG_GETARG_POINTER(3);
      76        1372 :     Pointer   **extra_data = (Pointer **) PG_GETARG_POINTER(4);
      77        1372 :     Datum      *entries = (Datum *) palloc(sizeof(Datum));
      78        1372 :     QueryInfo  *data = (QueryInfo *) palloc(sizeof(QueryInfo));
      79        1372 :     bool       *ptr_partialmatch = (bool *) palloc(sizeof(bool));
      80             :     int         btree_strat,
      81             :                 rhs_code;
      82             : 
      83             :     /*
      84             :      * Extract the btree strategy code and the RHS data type code from the
      85             :      * given strategy number.
      86             :      */
      87        1372 :     btree_strat = BTGIN_GET_BTREE_STRATEGY(strategy);
      88        1372 :     rhs_code = BTGIN_GET_RHS_TYPE_CODE(strategy);
      89             : 
      90             :     /*
      91             :      * Detoast the comparison datum.  This isn't necessary for correctness,
      92             :      * but it can save repeat detoastings within the comparison function.
      93             :      */
      94        1372 :     if (rhs_is_varlena[rhs_code])
      95         220 :         datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
      96             : 
      97             :     /* Prep single comparison key with possible partial-match flag */
      98        1372 :     *nentries = 1;
      99        1372 :     *partialmatch = ptr_partialmatch;
     100        1372 :     *ptr_partialmatch = false;
     101             : 
     102             :     /*
     103             :      * For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
     104             :      * BTEqualStrategyNumber we want to start the index scan at the supplied
     105             :      * query datum, and work forward.  For BTLessStrategyNumber and
     106             :      * BTLessEqualStrategyNumber, we need to start at the leftmost key, and
     107             :      * work forward until the supplied query datum (which we'll send along
     108             :      * inside the QueryInfo structure).  Use partial match rules except for
     109             :      * BTEqualStrategyNumber without a conversion function.  (If there is a
     110             :      * conversion function, comparison to the entry value is not trustworthy.)
     111             :      */
     112        1372 :     switch (btree_strat)
     113             :     {
     114         568 :         case BTLessStrategyNumber:
     115             :         case BTLessEqualStrategyNumber:
     116         568 :             entries[0] = leftmostvalue();
     117         568 :             *ptr_partialmatch = true;
     118         568 :             break;
     119         534 :         case BTGreaterEqualStrategyNumber:
     120             :         case BTGreaterStrategyNumber:
     121         534 :             *ptr_partialmatch = true;
     122             :             /* FALLTHROUGH */
     123         804 :         case BTEqualStrategyNumber:
     124             :             /* If we have a conversion function, apply it */
     125         804 :             if (cvt_fns && cvt_fns[rhs_code])
     126             :             {
     127         412 :                 entries[0] = (*cvt_fns[rhs_code]) (datum);
     128         412 :                 *ptr_partialmatch = true;
     129             :             }
     130             :             else
     131         392 :                 entries[0] = datum;
     132         804 :             break;
     133           0 :         default:
     134           0 :             elog(ERROR, "unrecognized strategy number: %d", strategy);
     135             :     }
     136             : 
     137             :     /* Fill "extra" data */
     138        1372 :     data->strategy = strategy;
     139        1372 :     data->orig_datum = datum;
     140        1372 :     data->entry_datum = entries[0];
     141        1372 :     data->typecmp = cmp_fns[rhs_code];
     142        1372 :     *extra_data = (Pointer *) palloc(sizeof(Pointer));
     143        1372 :     **extra_data = (Pointer) data;
     144             : 
     145        1372 :     PG_RETURN_POINTER(entries);
     146             : }
     147             : 
     148             : static Datum
     149        2188 : gin_btree_compare_prefix(FunctionCallInfo fcinfo)
     150             : {
     151        2188 :     Datum       partial_key PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(0);
     152        2188 :     Datum       key = PG_GETARG_DATUM(1);
     153        2188 :     QueryInfo  *data = (QueryInfo *) PG_GETARG_POINTER(3);
     154             :     int32       res,
     155             :                 cmp;
     156             : 
     157             :     /*
     158             :      * partial_key is only an approximation to the real comparison value,
     159             :      * especially if it's a leftmost value.  We can get an accurate answer by
     160             :      * doing a possibly-cross-type comparison to the real comparison value.
     161             :      * (Note that partial_key and key are of the indexed datatype while
     162             :      * orig_datum is of the query operator's RHS datatype.)
     163             :      *
     164             :      * But just to be sure that things are what we expect, let's assert that
     165             :      * partial_key is indeed what gin_btree_extract_query reported, so that
     166             :      * we'll notice if anyone ever changes the core code in a way that breaks
     167             :      * our assumptions.
     168             :      */
     169             :     Assert(partial_key == data->entry_datum);
     170             : 
     171        2188 :     cmp = DatumGetInt32(CallerFInfoFunctionCall2(data->typecmp,
     172             :                                                  fcinfo->flinfo,
     173             :                                                  PG_GET_COLLATION(),
     174             :                                                  data->orig_datum,
     175             :                                                  key));
     176             : 
     177             :     /*
     178             :      * Convert the comparison result to the correct thing for the search
     179             :      * operator strategy.  When dealing with cross-type comparisons, an
     180             :      * imprecise entry datum could lead GIN to start the scan just before the
     181             :      * first possible match, so we must continue the scan if the current index
     182             :      * entry doesn't satisfy the search condition for >= and > cases.  But if
     183             :      * that happens in an = search we can stop, because an imprecise entry
     184             :      * datum means that the search value is unrepresentable in the indexed
     185             :      * data type, so that there will be no exact matches.
     186             :      */
     187        2188 :     switch (BTGIN_GET_BTREE_STRATEGY(data->strategy))
     188             :     {
     189         544 :         case BTLessStrategyNumber:
     190             :             /* If original datum > indexed one then return match */
     191         544 :             if (cmp > 0)
     192         418 :                 res = 0;
     193             :             else
     194         126 :                 res = 1;        /* end scan */
     195         544 :             break;
     196         656 :         case BTLessEqualStrategyNumber:
     197             :             /* If original datum >= indexed one then return match */
     198         656 :             if (cmp >= 0)
     199         538 :                 res = 0;
     200             :             else
     201         118 :                 res = 1;        /* end scan */
     202         656 :             break;
     203         108 :         case BTEqualStrategyNumber:
     204             :             /* If original datum = indexed one then return match */
     205             :             /* See above about why we can end scan when cmp < 0 */
     206         108 :             if (cmp == 0)
     207          50 :                 res = 0;
     208             :             else
     209          58 :                 res = 1;        /* end scan */
     210         108 :             break;
     211         432 :         case BTGreaterEqualStrategyNumber:
     212             :             /* If original datum <= indexed one then return match */
     213         432 :             if (cmp <= 0)
     214         424 :                 res = 0;
     215             :             else
     216           8 :                 res = -1;       /* keep scanning */
     217         432 :             break;
     218         448 :         case BTGreaterStrategyNumber:
     219             :             /* If original datum < indexed one then return match */
     220         448 :             if (cmp < 0)
     221         328 :                 res = 0;
     222             :             else
     223         120 :                 res = -1;       /* keep scanning */
     224         448 :             break;
     225           0 :         default:
     226           0 :             elog(ERROR, "unrecognized strategy number: %d",
     227             :                  data->strategy);
     228             :             res = 0;
     229             :     }
     230             : 
     231        2188 :     PG_RETURN_INT32(res);
     232             : }
     233             : 
     234          60 : PG_FUNCTION_INFO_V1(gin_btree_consistent);
     235             : Datum
     236        1832 : gin_btree_consistent(PG_FUNCTION_ARGS)
     237             : {
     238        1832 :     bool       *recheck = (bool *) PG_GETARG_POINTER(5);
     239             : 
     240        1832 :     *recheck = false;
     241        1832 :     PG_RETURN_BOOL(true);
     242             : }
     243             : 
     244             : /*** GIN_SUPPORT macro defines the datatype specific functions ***/
     245             : 
     246             : #define GIN_SUPPORT(type, leftmostvalue, is_varlena, cvtfns, cmpfns)        \
     247             : PG_FUNCTION_INFO_V1(gin_extract_value_##type);                              \
     248             : Datum                                                                       \
     249             : gin_extract_value_##type(PG_FUNCTION_ARGS)                                  \
     250             : {                                                                           \
     251             :     return gin_btree_extract_value(fcinfo, is_varlena[0]);                  \
     252             : }   \
     253             : PG_FUNCTION_INFO_V1(gin_extract_query_##type);                              \
     254             : Datum                                                                       \
     255             : gin_extract_query_##type(PG_FUNCTION_ARGS)                                  \
     256             : {                                                                           \
     257             :     return gin_btree_extract_query(fcinfo,                                  \
     258             :                                    leftmostvalue, is_varlena,               \
     259             :                                    cvtfns, cmpfns);                         \
     260             : }   \
     261             : PG_FUNCTION_INFO_V1(gin_compare_prefix_##type);                             \
     262             : Datum                                                                       \
     263             : gin_compare_prefix_##type(PG_FUNCTION_ARGS)                                 \
     264             : {                                                                           \
     265             :     return gin_btree_compare_prefix(fcinfo);                                \
     266             : }
     267             : 
     268             : 
     269             : /*** Datatype specifications ***/
     270             : 
     271             : /* Function to produce the least possible value of the indexed datatype */
     272             : static Datum
     273          44 : leftmostvalue_int2(void)
     274             : {
     275          44 :     return Int16GetDatum(SHRT_MIN);
     276             : }
     277             : 
     278             : /*
     279             :  * For cross-type support, we must provide conversion functions that produce
     280             :  * a Datum of the indexed datatype, since GIN requires the "entry" datums to
     281             :  * be of that type.  If an exact conversion is not possible, produce a value
     282             :  * that will lead GIN to find the first index entry that is greater than
     283             :  * or equal to the actual comparison value.  (But rounding down is OK, so
     284             :  * sometimes we might find an index entry that's just less than the
     285             :  * comparison value.)
     286             :  *
     287             :  * For integer values, it's sufficient to clamp the input to be in-range.
     288             :  *
     289             :  * Note: for out-of-range input values, we could in theory detect that the
     290             :  * search condition matches all or none of the index, and avoid a useless
     291             :  * index descent in the latter case.  Such searches are probably rare though,
     292             :  * so we don't contort this code enough to do that.
     293             :  */
     294             : static Datum
     295          36 : cvt_int4_int2(Datum input)
     296             : {
     297          36 :     int32       val = DatumGetInt32(input);
     298             : 
     299          36 :     val = Max(val, SHRT_MIN);
     300          36 :     val = Min(val, SHRT_MAX);
     301          36 :     return Int16GetDatum((int16) val);
     302             : }
     303             : 
     304             : static Datum
     305          12 : cvt_int8_int2(Datum input)
     306             : {
     307          12 :     int64       val = DatumGetInt64(input);
     308             : 
     309          12 :     val = Max(val, SHRT_MIN);
     310          12 :     val = Min(val, SHRT_MAX);
     311          12 :     return Int16GetDatum((int16) val);
     312             : }
     313             : 
     314             : /*
     315             :  * RHS-type-is-varlena flags, conversion and comparison function arrays,
     316             :  * indexed by high bits of the operator strategy number.  A NULL in the
     317             :  * conversion function array indicates that no conversion is needed, which
     318             :  * will always be the case for the zero'th entry.  Note that the cross-type
     319             :  * comparison functions should be the ones with the indexed datatype second.
     320             :  */
     321             : static const bool int2_rhs_is_varlena[] =
     322             : {false, false, false};
     323             : 
     324             : static const btree_gin_convert_function int2_cvt_fns[] =
     325             : {NULL, cvt_int4_int2, cvt_int8_int2};
     326             : 
     327             : static const PGFunction int2_cmp_fns[] =
     328             : {btint2cmp, btint42cmp, btint82cmp};
     329             : 
     330         306 : GIN_SUPPORT(int2, leftmostvalue_int2, int2_rhs_is_varlena, int2_cvt_fns, int2_cmp_fns)
     331             : 
     332             : static Datum
     333          28 : leftmostvalue_int4(void)
     334             : {
     335          28 :     return Int32GetDatum(INT_MIN);
     336             : }
     337             : 
     338             : static Datum
     339          12 : cvt_int2_int4(Datum input)
     340             : {
     341          12 :     int16       val = DatumGetInt16(input);
     342             : 
     343          12 :     return Int32GetDatum((int32) val);
     344             : }
     345             : 
     346             : static Datum
     347          12 : cvt_int8_int4(Datum input)
     348             : {
     349          12 :     int64       val = DatumGetInt64(input);
     350             : 
     351          12 :     val = Max(val, INT_MIN);
     352          12 :     val = Min(val, INT_MAX);
     353          12 :     return Int32GetDatum((int32) val);
     354             : }
     355             : 
     356             : static const bool int4_rhs_is_varlena[] =
     357             : {false, false, false};
     358             : 
     359             : static const btree_gin_convert_function int4_cvt_fns[] =
     360             : {NULL, cvt_int2_int4, cvt_int8_int4};
     361             : 
     362             : static const PGFunction int4_cmp_fns[] =
     363             : {btint4cmp, btint24cmp, btint84cmp};
     364             : 
     365         186 : GIN_SUPPORT(int4, leftmostvalue_int4, int4_rhs_is_varlena, int4_cvt_fns, int4_cmp_fns)
     366             : 
     367             : static Datum
     368          28 : leftmostvalue_int8(void)
     369             : {
     370          28 :     return Int64GetDatum(PG_INT64_MIN);
     371             : }
     372             : 
     373             : static Datum
     374          12 : cvt_int2_int8(Datum input)
     375             : {
     376          12 :     int16       val = DatumGetInt16(input);
     377             : 
     378          12 :     return Int64GetDatum((int64) val);
     379             : }
     380             : 
     381             : static Datum
     382          12 : cvt_int4_int8(Datum input)
     383             : {
     384          12 :     int32       val = DatumGetInt32(input);
     385             : 
     386          12 :     return Int64GetDatum((int64) val);
     387             : }
     388             : 
     389             : static const bool int8_rhs_is_varlena[] =
     390             : {false, false, false};
     391             : 
     392             : static const btree_gin_convert_function int8_cvt_fns[] =
     393             : {NULL, cvt_int2_int8, cvt_int4_int8};
     394             : 
     395             : static const PGFunction int8_cmp_fns[] =
     396             : {btint8cmp, btint28cmp, btint48cmp};
     397             : 
     398         186 : GIN_SUPPORT(int8, leftmostvalue_int8, int8_rhs_is_varlena, int8_cvt_fns, int8_cmp_fns)
     399             : 
     400             : static Datum
     401          66 : leftmostvalue_float4(void)
     402             : {
     403          66 :     return Float4GetDatum(-get_float4_infinity());
     404             : }
     405             : 
     406             : static Datum
     407          84 : cvt_float8_float4(Datum input)
     408             : {
     409          84 :     float8      val = DatumGetFloat8(input);
     410             :     float4      result;
     411             : 
     412             :     /*
     413             :      * Assume that ordinary C conversion will produce a usable result.
     414             :      * (Compare dtof(), which raises error conditions that we don't need.)
     415             :      * Note that for inputs that aren't exactly representable as float4, it
     416             :      * doesn't matter whether the conversion rounds up or down.  That might
     417             :      * cause us to scan a few index entries that we'll reject as not matching,
     418             :      * but we won't miss any that should match.
     419             :      */
     420          84 :     result = (float4) val;
     421          84 :     return Float4GetDatum(result);
     422             : }
     423             : 
     424             : static const bool float4_rhs_is_varlena[] =
     425             : {false, false};
     426             : 
     427             : static const btree_gin_convert_function float4_cvt_fns[] =
     428             : {NULL, cvt_float8_float4};
     429             : 
     430             : static const PGFunction float4_cmp_fns[] =
     431             : {btfloat4cmp, btfloat84cmp};
     432             : 
     433         520 : GIN_SUPPORT(float4, leftmostvalue_float4, float4_rhs_is_varlena, float4_cvt_fns, float4_cmp_fns)
     434             : 
     435             : static Datum
     436          18 : leftmostvalue_float8(void)
     437             : {
     438          18 :     return Float8GetDatum(-get_float8_infinity());
     439             : }
     440             : 
     441             : static Datum
     442          12 : cvt_float4_float8(Datum input)
     443             : {
     444          12 :     float4      val = DatumGetFloat4(input);
     445             : 
     446          12 :     return Float8GetDatum((float8) val);
     447             : }
     448             : 
     449             : static const bool float8_rhs_is_varlena[] =
     450             : {false, false};
     451             : 
     452             : static const btree_gin_convert_function float8_cvt_fns[] =
     453             : {NULL, cvt_float4_float8};
     454             : 
     455             : static const PGFunction float8_cmp_fns[] =
     456             : {btfloat8cmp, btfloat48cmp};
     457             : 
     458         130 : GIN_SUPPORT(float8, leftmostvalue_float8, float8_rhs_is_varlena, float8_cvt_fns, float8_cmp_fns)
     459             : 
     460             : static Datum
     461           8 : leftmostvalue_money(void)
     462             : {
     463           8 :     return Int64GetDatum(PG_INT64_MIN);
     464             : }
     465             : 
     466             : static const bool money_rhs_is_varlena[] =
     467             : {false};
     468             : 
     469             : static const PGFunction money_cmp_fns[] =
     470             : {cash_cmp};
     471             : 
     472          74 : GIN_SUPPORT(money, leftmostvalue_money, money_rhs_is_varlena, NULL, money_cmp_fns)
     473             : 
     474             : static Datum
     475           8 : leftmostvalue_oid(void)
     476             : {
     477           8 :     return ObjectIdGetDatum(0);
     478             : }
     479             : 
     480             : static const bool oid_rhs_is_varlena[] =
     481             : {false};
     482             : 
     483             : static const PGFunction oid_cmp_fns[] =
     484             : {btoidcmp};
     485             : 
     486          74 : GIN_SUPPORT(oid, leftmostvalue_oid, oid_rhs_is_varlena, NULL, oid_cmp_fns)
     487             : 
     488             : static Datum
     489          92 : leftmostvalue_timestamp(void)
     490             : {
     491          92 :     return TimestampGetDatum(DT_NOBEGIN);
     492             : }
     493             : 
     494             : static Datum
     495          36 : cvt_date_timestamp(Datum input)
     496             : {
     497          36 :     DateADT     val = DatumGetDateADT(input);
     498             :     Timestamp   result;
     499             :     int         overflow;
     500             : 
     501          36 :     result = date2timestamp_opt_overflow(val, &overflow);
     502             :     /* We can ignore the overflow result, since result is useful as-is */
     503          36 :     return TimestampGetDatum(result);
     504             : }
     505             : 
     506             : static Datum
     507          40 : cvt_timestamptz_timestamp(Datum input)
     508             : {
     509          40 :     TimestampTz val = DatumGetTimestampTz(input);
     510             :     Timestamp   result;
     511             :     int         overflow;
     512             : 
     513          40 :     result = timestamptz2timestamp_opt_overflow(val, &overflow);
     514             :     /* We can ignore the overflow result, since result is useful as-is */
     515          40 :     return TimestampGetDatum(result);
     516             : }
     517             : 
     518             : static const bool timestamp_rhs_is_varlena[] =
     519             : {false, false, false};
     520             : 
     521             : static const btree_gin_convert_function timestamp_cvt_fns[] =
     522             : {NULL, cvt_date_timestamp, cvt_timestamptz_timestamp};
     523             : 
     524             : static const PGFunction timestamp_cmp_fns[] =
     525             : {timestamp_cmp, date_cmp_timestamp, timestamptz_cmp_timestamp};
     526             : 
     527         454 : GIN_SUPPORT(timestamp, leftmostvalue_timestamp, timestamp_rhs_is_varlena, timestamp_cvt_fns, timestamp_cmp_fns)
     528             : 
     529             : static Datum
     530          12 : cvt_date_timestamptz(Datum input)
     531             : {
     532          12 :     DateADT     val = DatumGetDateADT(input);
     533             :     TimestampTz result;
     534             :     int         overflow;
     535             : 
     536          12 :     result = date2timestamptz_opt_overflow(val, &overflow);
     537             :     /* We can ignore the overflow result, since result is useful as-is */
     538          12 :     return TimestampTzGetDatum(result);
     539             : }
     540             : 
     541             : static Datum
     542          12 : cvt_timestamp_timestamptz(Datum input)
     543             : {
     544          12 :     Timestamp   val = DatumGetTimestamp(input);
     545             :     TimestampTz result;
     546             :     int         overflow;
     547             : 
     548          12 :     result = timestamp2timestamptz_opt_overflow(val, &overflow);
     549             :     /* We can ignore the overflow result, since result is useful as-is */
     550          12 :     return TimestampTzGetDatum(result);
     551             : }
     552             : 
     553             : static const bool timestamptz_rhs_is_varlena[] =
     554             : {false, false, false};
     555             : 
     556             : static const btree_gin_convert_function timestamptz_cvt_fns[] =
     557             : {NULL, cvt_date_timestamptz, cvt_timestamp_timestamptz};
     558             : 
     559             : static const PGFunction timestamptz_cmp_fns[] =
     560             : {timestamp_cmp, date_cmp_timestamptz, timestamp_cmp_timestamptz};
     561             : 
     562         182 : GIN_SUPPORT(timestamptz, leftmostvalue_timestamp, timestamptz_rhs_is_varlena, timestamptz_cvt_fns, timestamptz_cmp_fns)
     563             : 
     564             : static Datum
     565           8 : leftmostvalue_time(void)
     566             : {
     567           8 :     return TimeADTGetDatum(0);
     568             : }
     569             : 
     570             : static const bool time_rhs_is_varlena[] =
     571             : {false};
     572             : 
     573             : static const PGFunction time_cmp_fns[] =
     574             : {time_cmp};
     575             : 
     576          74 : GIN_SUPPORT(time, leftmostvalue_time, time_rhs_is_varlena, NULL, time_cmp_fns)
     577             : 
     578             : static Datum
     579           8 : leftmostvalue_timetz(void)
     580             : {
     581           8 :     TimeTzADT  *v = palloc(sizeof(TimeTzADT));
     582             : 
     583           8 :     v->time = 0;
     584           8 :     v->zone = -24 * 3600;        /* XXX is that true? */
     585             : 
     586           8 :     return TimeTzADTPGetDatum(v);
     587             : }
     588             : 
     589             : static const bool timetz_rhs_is_varlena[] =
     590             : {false};
     591             : 
     592             : static const PGFunction timetz_cmp_fns[] =
     593             : {timetz_cmp};
     594             : 
     595          74 : GIN_SUPPORT(timetz, leftmostvalue_timetz, timetz_rhs_is_varlena, NULL, timetz_cmp_fns)
     596             : 
     597             : static Datum
     598          76 : leftmostvalue_date(void)
     599             : {
     600          76 :     return DateADTGetDatum(DATEVAL_NOBEGIN);
     601             : }
     602             : 
     603             : static Datum
     604          48 : cvt_timestamp_date(Datum input)
     605             : {
     606          48 :     Timestamp   val = DatumGetTimestamp(input);
     607             :     DateADT     result;
     608             :     int         overflow;
     609             : 
     610          48 :     result = timestamp2date_opt_overflow(val, &overflow);
     611             :     /* We can ignore the overflow result, since result is useful as-is */
     612          48 :     return DateADTGetDatum(result);
     613             : }
     614             : 
     615             : static Datum
     616          48 : cvt_timestamptz_date(Datum input)
     617             : {
     618          48 :     TimestampTz val = DatumGetTimestampTz(input);
     619             :     DateADT     result;
     620             :     int         overflow;
     621             : 
     622          48 :     result = timestamptz2date_opt_overflow(val, &overflow);
     623             :     /* We can ignore the overflow result, since result is useful as-is */
     624          48 :     return DateADTGetDatum(result);
     625             : }
     626             : 
     627             : static const bool date_rhs_is_varlena[] =
     628             : {false, false, false};
     629             : 
     630             : static const btree_gin_convert_function date_cvt_fns[] =
     631             : {NULL, cvt_timestamp_date, cvt_timestamptz_date};
     632             : 
     633             : static const PGFunction date_cmp_fns[] =
     634             : {date_cmp, timestamp_cmp_date, timestamptz_cmp_date};
     635             : 
     636         554 : GIN_SUPPORT(date, leftmostvalue_date, date_rhs_is_varlena, date_cvt_fns, date_cmp_fns)
     637             : 
     638             : static Datum
     639           8 : leftmostvalue_interval(void)
     640             : {
     641           8 :     Interval   *v = palloc(sizeof(Interval));
     642             : 
     643           8 :     INTERVAL_NOBEGIN(v);
     644             : 
     645           8 :     return IntervalPGetDatum(v);
     646             : }
     647             : 
     648             : static const bool interval_rhs_is_varlena[] =
     649             : {false};
     650             : 
     651             : static const PGFunction interval_cmp_fns[] =
     652             : {interval_cmp};
     653             : 
     654          86 : GIN_SUPPORT(interval, leftmostvalue_interval, interval_rhs_is_varlena, NULL, interval_cmp_fns)
     655             : 
     656             : static Datum
     657           8 : leftmostvalue_macaddr(void)
     658             : {
     659           8 :     macaddr    *v = palloc0(sizeof(macaddr));
     660             : 
     661           8 :     return MacaddrPGetDatum(v);
     662             : }
     663             : 
     664             : static const bool macaddr_rhs_is_varlena[] =
     665             : {false};
     666             : 
     667             : static const PGFunction macaddr_cmp_fns[] =
     668             : {macaddr_cmp};
     669             : 
     670          74 : GIN_SUPPORT(macaddr, leftmostvalue_macaddr, macaddr_rhs_is_varlena, NULL, macaddr_cmp_fns)
     671             : 
     672             : static Datum
     673           8 : leftmostvalue_macaddr8(void)
     674             : {
     675           8 :     macaddr8   *v = palloc0(sizeof(macaddr8));
     676             : 
     677           8 :     return Macaddr8PGetDatum(v);
     678             : }
     679             : 
     680             : static const bool macaddr8_rhs_is_varlena[] =
     681             : {false};
     682             : 
     683             : static const PGFunction macaddr8_cmp_fns[] =
     684             : {macaddr8_cmp};
     685             : 
     686          74 : GIN_SUPPORT(macaddr8, leftmostvalue_macaddr8, macaddr8_rhs_is_varlena, NULL, macaddr8_cmp_fns)
     687             : 
     688             : static Datum
     689          16 : leftmostvalue_inet(void)
     690             : {
     691          16 :     return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
     692             : }
     693             : 
     694             : static const bool inet_rhs_is_varlena[] =
     695             : {true};
     696             : 
     697             : static const PGFunction inet_cmp_fns[] =
     698             : {network_cmp};
     699             : 
     700          74 : GIN_SUPPORT(inet, leftmostvalue_inet, inet_rhs_is_varlena, NULL, inet_cmp_fns)
     701             : 
     702             : static const bool cidr_rhs_is_varlena[] =
     703             : {true};
     704             : 
     705             : static const PGFunction cidr_cmp_fns[] =
     706             : {network_cmp};
     707             : 
     708          74 : GIN_SUPPORT(cidr, leftmostvalue_inet, cidr_rhs_is_varlena, NULL, cidr_cmp_fns)
     709             : 
     710             : static Datum
     711          46 : leftmostvalue_text(void)
     712             : {
     713          46 :     return PointerGetDatum(cstring_to_text_with_len("", 0));
     714             : }
     715             : 
     716             : static Datum
     717          12 : cvt_name_text(Datum input)
     718             : {
     719          12 :     Name        val = DatumGetName(input);
     720             : 
     721          12 :     return PointerGetDatum(cstring_to_text(NameStr(*val)));
     722             : }
     723             : 
     724             : static const bool text_rhs_is_varlena[] =
     725             : {true, false};
     726             : 
     727             : static const btree_gin_convert_function text_cvt_fns[] =
     728             : {NULL, cvt_name_text};
     729             : 
     730             : static const PGFunction text_cmp_fns[] =
     731             : {bttextcmp, btnametextcmp};
     732             : 
     733         198 : GIN_SUPPORT(text, leftmostvalue_text, text_rhs_is_varlena, text_cvt_fns, text_cmp_fns)
     734             : 
     735             : static const bool bpchar_rhs_is_varlena[] =
     736             : {true};
     737             : 
     738             : static const PGFunction bpchar_cmp_fns[] =
     739             : {bpcharcmp};
     740             : 
     741          92 : GIN_SUPPORT(bpchar, leftmostvalue_text, bpchar_rhs_is_varlena, NULL, bpchar_cmp_fns)
     742             : 
     743             : static Datum
     744           8 : leftmostvalue_char(void)
     745             : {
     746           8 :     return CharGetDatum(0);
     747             : }
     748             : 
     749             : static const bool char_rhs_is_varlena[] =
     750             : {false};
     751             : 
     752             : static const PGFunction char_cmp_fns[] =
     753             : {btcharcmp};
     754             : 
     755          74 : GIN_SUPPORT(char, leftmostvalue_char, char_rhs_is_varlena, NULL, char_cmp_fns)
     756             : 
     757             : static const bool bytea_rhs_is_varlena[] =
     758             : {true};
     759             : 
     760             : static const PGFunction bytea_cmp_fns[] =
     761             : {byteacmp};
     762             : 
     763          74 : GIN_SUPPORT(bytea, leftmostvalue_text, bytea_rhs_is_varlena, NULL, bytea_cmp_fns)
     764             : 
     765             : static Datum
     766           8 : leftmostvalue_bit(void)
     767             : {
     768           8 :     return DirectFunctionCall3(bit_in,
     769             :                                CStringGetDatum(""),
     770             :                                ObjectIdGetDatum(0),
     771             :                                Int32GetDatum(-1));
     772             : }
     773             : 
     774             : static const bool bit_rhs_is_varlena[] =
     775             : {true};
     776             : 
     777             : static const PGFunction bit_cmp_fns[] =
     778             : {bitcmp};
     779             : 
     780          74 : GIN_SUPPORT(bit, leftmostvalue_bit, bit_rhs_is_varlena, NULL, bit_cmp_fns)
     781             : 
     782             : static Datum
     783           8 : leftmostvalue_varbit(void)
     784             : {
     785           8 :     return DirectFunctionCall3(varbit_in,
     786             :                                CStringGetDatum(""),
     787             :                                ObjectIdGetDatum(0),
     788             :                                Int32GetDatum(-1));
     789             : }
     790             : 
     791             : static const bool varbit_rhs_is_varlena[] =
     792             : {true};
     793             : 
     794             : static const PGFunction varbit_cmp_fns[] =
     795             : {bitcmp};
     796             : 
     797          74 : GIN_SUPPORT(varbit, leftmostvalue_varbit, varbit_rhs_is_varlena, NULL, varbit_cmp_fns)
     798             : 
     799             : /*
     800             :  * Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
     801             :  * (*not* a SQL NULL) to represent that.  We can get away with that because
     802             :  * the value returned by our leftmostvalue function will never be stored in
     803             :  * the index nor passed to anything except our compare and prefix-comparison
     804             :  * functions.  The same trick could be used for other pass-by-reference types.
     805             :  */
     806             : 
     807             : #define NUMERIC_IS_LEFTMOST(x)  ((x) == NULL)
     808             : 
     809           4 : PG_FUNCTION_INFO_V1(gin_numeric_cmp);
     810             : 
     811             : Datum
     812          86 : gin_numeric_cmp(PG_FUNCTION_ARGS)
     813             : {
     814          86 :     Numeric     a = (Numeric) PG_GETARG_POINTER(0);
     815          86 :     Numeric     b = (Numeric) PG_GETARG_POINTER(1);
     816          86 :     int         res = 0;
     817             : 
     818          86 :     if (NUMERIC_IS_LEFTMOST(a))
     819             :     {
     820          12 :         res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
     821             :     }
     822          74 :     else if (NUMERIC_IS_LEFTMOST(b))
     823             :     {
     824           0 :         res = 1;
     825             :     }
     826             :     else
     827             :     {
     828          74 :         res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
     829             :                                                 NumericGetDatum(a),
     830             :                                                 NumericGetDatum(b)));
     831             :     }
     832             : 
     833          86 :     PG_RETURN_INT32(res);
     834             : }
     835             : 
     836             : static Datum
     837           8 : leftmostvalue_numeric(void)
     838             : {
     839           8 :     return PointerGetDatum(NULL);
     840             : }
     841             : 
     842             : static const bool numeric_rhs_is_varlena[] =
     843             : {true};
     844             : 
     845             : static const PGFunction numeric_cmp_fns[] =
     846             : {gin_numeric_cmp};
     847             : 
     848          74 : GIN_SUPPORT(numeric, leftmostvalue_numeric, numeric_rhs_is_varlena, NULL, numeric_cmp_fns)
     849             : 
     850             : /*
     851             :  * Use a similar trick to that used for numeric for enums, since we don't
     852             :  * actually know the leftmost value of any enum without knowing the concrete
     853             :  * type, so we use a dummy leftmost value of InvalidOid.
     854             :  *
     855             :  * Note that we use CallerFInfoFunctionCall2 here so that enum_cmp
     856             :  * gets a valid fn_extra to work with. Unlike most other type comparison
     857             :  * routines it needs it, so we can't use DirectFunctionCall2.
     858             :  */
     859             : 
     860             : #define ENUM_IS_LEFTMOST(x) ((x) == InvalidOid)
     861             : 
     862           4 : PG_FUNCTION_INFO_V1(gin_enum_cmp);
     863             : 
     864             : Datum
     865      400104 : gin_enum_cmp(PG_FUNCTION_ARGS)
     866             : {
     867      400104 :     Oid         a = PG_GETARG_OID(0);
     868      400104 :     Oid         b = PG_GETARG_OID(1);
     869      400104 :     int         res = 0;
     870             : 
     871      400104 :     if (ENUM_IS_LEFTMOST(a))
     872             :     {
     873          12 :         res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
     874             :     }
     875      400092 :     else if (ENUM_IS_LEFTMOST(b))
     876             :     {
     877           0 :         res = 1;
     878             :     }
     879             :     else
     880             :     {
     881      400092 :         res = DatumGetInt32(CallerFInfoFunctionCall2(enum_cmp,
     882             :                                                      fcinfo->flinfo,
     883             :                                                      PG_GET_COLLATION(),
     884             :                                                      ObjectIdGetDatum(a),
     885             :                                                      ObjectIdGetDatum(b)));
     886             :     }
     887             : 
     888      400104 :     PG_RETURN_INT32(res);
     889             : }
     890             : 
     891             : static Datum
     892           8 : leftmostvalue_enum(void)
     893             : {
     894           8 :     return ObjectIdGetDatum(InvalidOid);
     895             : }
     896             : 
     897             : static const bool enum_rhs_is_varlena[] =
     898             : {false};
     899             : 
     900             : static const PGFunction enum_cmp_fns[] =
     901             : {gin_enum_cmp};
     902             : 
     903      200084 : GIN_SUPPORT(anyenum, leftmostvalue_enum, enum_rhs_is_varlena, NULL, enum_cmp_fns)
     904             : 
     905             : static Datum
     906          12 : leftmostvalue_uuid(void)
     907             : {
     908             :     /*
     909             :      * palloc0 will create the UUID with all zeroes:
     910             :      * "00000000-0000-0000-0000-000000000000"
     911             :      */
     912          12 :     pg_uuid_t  *retval = (pg_uuid_t *) palloc0(sizeof(pg_uuid_t));
     913             : 
     914          12 :     return UUIDPGetDatum(retval);
     915             : }
     916             : 
     917             : static const bool uuid_rhs_is_varlena[] =
     918             : {false};
     919             : 
     920             : static const PGFunction uuid_cmp_fns[] =
     921             : {uuid_cmp};
     922             : 
     923          84 : GIN_SUPPORT(uuid, leftmostvalue_uuid, uuid_rhs_is_varlena, NULL, uuid_cmp_fns)
     924             : 
     925             : static Datum
     926          26 : leftmostvalue_name(void)
     927             : {
     928          26 :     NameData   *result = (NameData *) palloc0(NAMEDATALEN);
     929             : 
     930          26 :     return NameGetDatum(result);
     931             : }
     932             : 
     933             : static Datum
     934          12 : cvt_text_name(Datum input)
     935             : {
     936          12 :     text       *val = DatumGetTextPP(input);
     937          12 :     NameData   *result = (NameData *) palloc0(NAMEDATALEN);
     938          12 :     int         len = VARSIZE_ANY_EXHDR(val);
     939             : 
     940             :     /*
     941             :      * Truncate oversize input.  We're assuming this will produce a result
     942             :      * considered less than the original.  That could be a bad assumption in
     943             :      * some collations, but fortunately an index on "name" is generally going
     944             :      * to use C collation.
     945             :      */
     946          12 :     if (len >= NAMEDATALEN)
     947           0 :         len = pg_mbcliplen(VARDATA_ANY(val), len, NAMEDATALEN - 1);
     948             : 
     949          12 :     memcpy(NameStr(*result), VARDATA_ANY(val), len);
     950             : 
     951          12 :     return NameGetDatum(result);
     952             : }
     953             : 
     954             : static const bool name_rhs_is_varlena[] =
     955             : {false, true};
     956             : 
     957             : static const btree_gin_convert_function name_cvt_fns[] =
     958             : {NULL, cvt_text_name};
     959             : 
     960             : static const PGFunction name_cmp_fns[] =
     961             : {btnamecmp, bttextnamecmp};
     962             : 
     963         154 : GIN_SUPPORT(name, leftmostvalue_name, name_rhs_is_varlena, name_cvt_fns, name_cmp_fns)
     964             : 
     965             : static Datum
     966          20 : leftmostvalue_bool(void)
     967             : {
     968          20 :     return BoolGetDatum(false);
     969             : }
     970             : 
     971             : static const bool bool_rhs_is_varlena[] =
     972             : {false};
     973             : 
     974             : static const PGFunction bool_cmp_fns[] =
     975             : {btboolcmp};
     976             : 
     977          94 : GIN_SUPPORT(bool, leftmostvalue_bool, bool_rhs_is_varlena, NULL, bool_cmp_fns)

Generated by: LCOV version 1.16