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

Generated by: LCOV version 2.0-1