LCOV - code coverage report
Current view: top level - contrib/btree_gin - btree_gin.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 259 266 97.4 %
Date: 2025-12-22 10:18:16 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 "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          62 : 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      201370 : gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
      52             : {
      53      201370 :     Datum       datum = PG_GETARG_DATUM(0);
      54      201370 :     int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
      55      201370 :     Datum      *entries = palloc_object(Datum);
      56             : 
      57             :     /* Ensure that values stored in the index are not toasted */
      58      201370 :     if (is_varlena)
      59         112 :         datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
      60      201370 :     entries[0] = datum;
      61      201370 :     *nentries = 1;
      62             : 
      63      201370 :     PG_RETURN_POINTER(entries);
      64             : }
      65             : 
      66             : static Datum
      67        1380 : 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        1380 :     Datum       datum = PG_GETARG_DATUM(0);
      74        1380 :     int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
      75        1380 :     StrategyNumber strategy = PG_GETARG_UINT16(2);
      76        1380 :     bool      **partialmatch = (bool **) PG_GETARG_POINTER(3);
      77        1380 :     Pointer   **extra_data = (Pointer **) PG_GETARG_POINTER(4);
      78        1380 :     Datum      *entries = palloc_object(Datum);
      79        1380 :     QueryInfo  *data = palloc_object(QueryInfo);
      80        1380 :     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        1380 :     btree_strat = BTGIN_GET_BTREE_STRATEGY(strategy);
      89        1380 :     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        1380 :     if (rhs_is_varlena[rhs_code])
      96         220 :         datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
      97             : 
      98             :     /* Prep single comparison key with possible partial-match flag */
      99        1380 :     *nentries = 1;
     100        1380 :     *partialmatch = ptr_partialmatch;
     101        1380 :     *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        1380 :     switch (btree_strat)
     114             :     {
     115         568 :         case BTLessStrategyNumber:
     116             :         case BTLessEqualStrategyNumber:
     117         568 :             entries[0] = leftmostvalue();
     118         568 :             *ptr_partialmatch = true;
     119         568 :             break;
     120         534 :         case BTGreaterEqualStrategyNumber:
     121             :         case BTGreaterStrategyNumber:
     122         534 :             *ptr_partialmatch = true;
     123             :             /* FALLTHROUGH */
     124         812 :         case BTEqualStrategyNumber:
     125             :             /* If we have a conversion function, apply it */
     126         812 :             if (cvt_fns && cvt_fns[rhs_code])
     127             :             {
     128         412 :                 entries[0] = (*cvt_fns[rhs_code]) (datum);
     129         412 :                 *ptr_partialmatch = true;
     130             :             }
     131             :             else
     132         400 :                 entries[0] = datum;
     133         812 :             break;
     134           0 :         default:
     135           0 :             elog(ERROR, "unrecognized strategy number: %d", strategy);
     136             :     }
     137             : 
     138             :     /* Fill "extra" data */
     139        1380 :     data->strategy = strategy;
     140        1380 :     data->orig_datum = datum;
     141        1380 :     data->entry_datum = entries[0];
     142        1380 :     data->typecmp = cmp_fns[rhs_code];
     143        1380 :     *extra_data = palloc_object(Pointer);
     144        1380 :     **extra_data = (Pointer) data;
     145             : 
     146        1380 :     PG_RETURN_POINTER(entries);
     147             : }
     148             : 
     149             : static Datum
     150        2188 : gin_btree_compare_prefix(FunctionCallInfo fcinfo)
     151             : {
     152        2188 :     Datum       partial_key PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(0);
     153        2188 :     Datum       key = PG_GETARG_DATUM(1);
     154        2188 :     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        2188 :     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        2188 :     switch (BTGIN_GET_BTREE_STRATEGY(data->strategy))
     189             :     {
     190         544 :         case BTLessStrategyNumber:
     191             :             /* If original datum > indexed one then return match */
     192         544 :             if (cmp > 0)
     193         418 :                 res = 0;
     194             :             else
     195         126 :                 res = 1;        /* end scan */
     196         544 :             break;
     197         656 :         case BTLessEqualStrategyNumber:
     198             :             /* If original datum >= indexed one then return match */
     199         656 :             if (cmp >= 0)
     200         538 :                 res = 0;
     201             :             else
     202         118 :                 res = 1;        /* end scan */
     203         656 :             break;
     204         108 :         case BTEqualStrategyNumber:
     205             :             /* If original datum = indexed one then return match */
     206             :             /* See above about why we can end scan when cmp < 0 */
     207         108 :             if (cmp == 0)
     208          50 :                 res = 0;
     209             :             else
     210          58 :                 res = 1;        /* end scan */
     211         108 :             break;
     212         432 :         case BTGreaterEqualStrategyNumber:
     213             :             /* If original datum <= indexed one then return match */
     214         432 :             if (cmp <= 0)
     215         424 :                 res = 0;
     216             :             else
     217           8 :                 res = -1;       /* keep scanning */
     218         432 :             break;
     219         448 :         case BTGreaterStrategyNumber:
     220             :             /* If original datum < indexed one then return match */
     221         448 :             if (cmp < 0)
     222         328 :                 res = 0;
     223             :             else
     224         120 :                 res = -1;       /* keep scanning */
     225         448 :             break;
     226           0 :         default:
     227           0 :             elog(ERROR, "unrecognized strategy number: %d",
     228             :                  data->strategy);
     229             :             res = 0;
     230             :     }
     231             : 
     232        2188 :     PG_RETURN_INT32(res);
     233             : }
     234             : 
     235          64 : PG_FUNCTION_INFO_V1(gin_btree_consistent);
     236             : Datum
     237        1836 : gin_btree_consistent(PG_FUNCTION_ARGS)
     238             : {
     239        1836 :     bool       *recheck = (bool *) PG_GETARG_POINTER(5);
     240             : 
     241        1836 :     *recheck = false;
     242        1836 :     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          44 : leftmostvalue_int2(void)
     275             : {
     276          44 :     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          36 : cvt_int4_int2(Datum input)
     297             : {
     298          36 :     int32       val = DatumGetInt32(input);
     299             : 
     300          36 :     val = Max(val, SHRT_MIN);
     301          36 :     val = Min(val, SHRT_MAX);
     302          36 :     return Int16GetDatum((int16) val);
     303             : }
     304             : 
     305             : static Datum
     306          12 : cvt_int8_int2(Datum input)
     307             : {
     308          12 :     int64       val = DatumGetInt64(input);
     309             : 
     310          12 :     val = Max(val, SHRT_MIN);
     311          12 :     val = Min(val, SHRT_MAX);
     312          12 :     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         312 : GIN_SUPPORT(int2, leftmostvalue_int2, int2_rhs_is_varlena, int2_cvt_fns, int2_cmp_fns)
     332             : 
     333             : static Datum
     334          28 : leftmostvalue_int4(void)
     335             : {
     336          28 :     return Int32GetDatum(INT_MIN);
     337             : }
     338             : 
     339             : static Datum
     340          12 : cvt_int2_int4(Datum input)
     341             : {
     342          12 :     int16       val = DatumGetInt16(input);
     343             : 
     344          12 :     return Int32GetDatum((int32) val);
     345             : }
     346             : 
     347             : static Datum
     348          12 : cvt_int8_int4(Datum input)
     349             : {
     350          12 :     int64       val = DatumGetInt64(input);
     351             : 
     352          12 :     val = Max(val, INT_MIN);
     353          12 :     val = Min(val, INT_MAX);
     354          12 :     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        1206 : GIN_SUPPORT(int4, leftmostvalue_int4, int4_rhs_is_varlena, int4_cvt_fns, int4_cmp_fns)
     367             : 
     368             : static Datum
     369          28 : leftmostvalue_int8(void)
     370             : {
     371          28 :     return Int64GetDatum(PG_INT64_MIN);
     372             : }
     373             : 
     374             : static Datum
     375          12 : cvt_int2_int8(Datum input)
     376             : {
     377          12 :     int16       val = DatumGetInt16(input);
     378             : 
     379          12 :     return Int64GetDatum((int64) val);
     380             : }
     381             : 
     382             : static Datum
     383          12 : cvt_int4_int8(Datum input)
     384             : {
     385          12 :     int32       val = DatumGetInt32(input);
     386             : 
     387          12 :     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         192 : GIN_SUPPORT(int8, leftmostvalue_int8, int8_rhs_is_varlena, int8_cvt_fns, int8_cmp_fns)
     400             : 
     401             : static Datum
     402          66 : leftmostvalue_float4(void)
     403             : {
     404          66 :     return Float4GetDatum(-get_float4_infinity());
     405             : }
     406             : 
     407             : static Datum
     408          84 : cvt_float8_float4(Datum input)
     409             : {
     410          84 :     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          84 :     result = (float4) val;
     422          84 :     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         526 : GIN_SUPPORT(float4, leftmostvalue_float4, float4_rhs_is_varlena, float4_cvt_fns, float4_cmp_fns)
     435             : 
     436             : static Datum
     437          18 : leftmostvalue_float8(void)
     438             : {
     439          18 :     return Float8GetDatum(-get_float8_infinity());
     440             : }
     441             : 
     442             : static Datum
     443          12 : cvt_float4_float8(Datum input)
     444             : {
     445          12 :     float4      val = DatumGetFloat4(input);
     446             : 
     447          12 :     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         136 : GIN_SUPPORT(float8, leftmostvalue_float8, float8_rhs_is_varlena, float8_cvt_fns, float8_cmp_fns)
     460             : 
     461             : static Datum
     462           8 : leftmostvalue_money(void)
     463             : {
     464           8 :     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          80 : GIN_SUPPORT(money, leftmostvalue_money, money_rhs_is_varlena, NULL, money_cmp_fns)
     474             : 
     475             : static Datum
     476           8 : leftmostvalue_oid(void)
     477             : {
     478           8 :     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          80 : GIN_SUPPORT(oid, leftmostvalue_oid, oid_rhs_is_varlena, NULL, oid_cmp_fns)
     488             : 
     489             : static Datum
     490          92 : leftmostvalue_timestamp(void)
     491             : {
     492          92 :     return TimestampGetDatum(DT_NOBEGIN);
     493             : }
     494             : 
     495             : static Datum
     496          36 : cvt_date_timestamp(Datum input)
     497             : {
     498          36 :     DateADT     val = DatumGetDateADT(input);
     499             :     Timestamp   result;
     500          36 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
     501             : 
     502          36 :     result = date2timestamp_safe(val, (Node *) &escontext);
     503             :     /* We can ignore errors, since result is useful as-is */
     504          36 :     return TimestampGetDatum(result);
     505             : }
     506             : 
     507             : static Datum
     508          40 : cvt_timestamptz_timestamp(Datum input)
     509             : {
     510          40 :     TimestampTz val = DatumGetTimestampTz(input);
     511          40 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
     512             :     Timestamp   result;
     513             : 
     514          40 :     result = timestamptz2timestamp_safe(val, (Node *) &escontext);
     515             :     /* We can ignore errors, since result is useful as-is */
     516          40 :     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         460 : GIN_SUPPORT(timestamp, leftmostvalue_timestamp, timestamp_rhs_is_varlena, timestamp_cvt_fns, timestamp_cmp_fns)
     529             : 
     530             : static Datum
     531          12 : cvt_date_timestamptz(Datum input)
     532             : {
     533          12 :     DateADT     val = DatumGetDateADT(input);
     534          12 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
     535             :     TimestampTz result;
     536             : 
     537          12 :     result = date2timestamptz_safe(val, (Node *) &escontext);
     538             :     /* We can ignore errors, since result is useful as-is */
     539          12 :     return TimestampTzGetDatum(result);
     540             : }
     541             : 
     542             : static Datum
     543          12 : cvt_timestamp_timestamptz(Datum input)
     544             : {
     545          12 :     Timestamp   val = DatumGetTimestamp(input);
     546          12 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
     547             :     TimestampTz result;
     548             : 
     549          12 :     result = timestamp2timestamptz_safe(val, (Node *) &escontext);
     550             :     /* We can ignore errors, since result is useful as-is */
     551          12 :     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         188 : GIN_SUPPORT(timestamptz, leftmostvalue_timestamp, timestamptz_rhs_is_varlena, timestamptz_cvt_fns, timestamptz_cmp_fns)
     564             : 
     565             : static Datum
     566           8 : leftmostvalue_time(void)
     567             : {
     568           8 :     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          80 : GIN_SUPPORT(time, leftmostvalue_time, time_rhs_is_varlena, NULL, time_cmp_fns)
     578             : 
     579             : static Datum
     580           8 : leftmostvalue_timetz(void)
     581             : {
     582           8 :     TimeTzADT  *v = palloc_object(TimeTzADT);
     583             : 
     584           8 :     v->time = 0;
     585           8 :     v->zone = -24 * 3600;        /* XXX is that true? */
     586             : 
     587           8 :     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          80 : GIN_SUPPORT(timetz, leftmostvalue_timetz, timetz_rhs_is_varlena, NULL, timetz_cmp_fns)
     597             : 
     598             : static Datum
     599          76 : leftmostvalue_date(void)
     600             : {
     601          76 :     return DateADTGetDatum(DATEVAL_NOBEGIN);
     602             : }
     603             : 
     604             : static Datum
     605          48 : cvt_timestamp_date(Datum input)
     606             : {
     607          48 :     Timestamp   val = DatumGetTimestamp(input);
     608          48 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
     609             :     DateADT     result;
     610             : 
     611          48 :     result = timestamp2date_safe(val, (Node *) &escontext);
     612             :     /* We can ignore errors, since result is useful as-is */
     613          48 :     return DateADTGetDatum(result);
     614             : }
     615             : 
     616             : static Datum
     617          48 : cvt_timestamptz_date(Datum input)
     618             : {
     619          48 :     TimestampTz val = DatumGetTimestampTz(input);
     620          48 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
     621             :     DateADT     result;
     622             : 
     623          48 :     result = timestamptz2date_safe(val, (Node *) &escontext);
     624             :     /* We can ignore errors, since result is useful as-is */
     625          48 :     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         560 : GIN_SUPPORT(date, leftmostvalue_date, date_rhs_is_varlena, date_cvt_fns, date_cmp_fns)
     638             : 
     639             : static Datum
     640           8 : leftmostvalue_interval(void)
     641             : {
     642           8 :     Interval   *v = palloc_object(Interval);
     643             : 
     644           8 :     INTERVAL_NOBEGIN(v);
     645             : 
     646           8 :     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          92 : GIN_SUPPORT(interval, leftmostvalue_interval, interval_rhs_is_varlena, NULL, interval_cmp_fns)
     656             : 
     657             : static Datum
     658           8 : leftmostvalue_macaddr(void)
     659             : {
     660           8 :     macaddr    *v = palloc0_object(macaddr);
     661             : 
     662           8 :     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          80 : GIN_SUPPORT(macaddr, leftmostvalue_macaddr, macaddr_rhs_is_varlena, NULL, macaddr_cmp_fns)
     672             : 
     673             : static Datum
     674           8 : leftmostvalue_macaddr8(void)
     675             : {
     676           8 :     macaddr8   *v = palloc0_object(macaddr8);
     677             : 
     678           8 :     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          80 : GIN_SUPPORT(macaddr8, leftmostvalue_macaddr8, macaddr8_rhs_is_varlena, NULL, macaddr8_cmp_fns)
     688             : 
     689             : static Datum
     690          16 : leftmostvalue_inet(void)
     691             : {
     692          16 :     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          80 : 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          80 : GIN_SUPPORT(cidr, leftmostvalue_inet, cidr_rhs_is_varlena, NULL, cidr_cmp_fns)
     710             : 
     711             : static Datum
     712          46 : leftmostvalue_text(void)
     713             : {
     714          46 :     return PointerGetDatum(cstring_to_text_with_len("", 0));
     715             : }
     716             : 
     717             : static Datum
     718          12 : cvt_name_text(Datum input)
     719             : {
     720          12 :     Name        val = DatumGetName(input);
     721             : 
     722          12 :     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         204 : 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          98 : GIN_SUPPORT(bpchar, leftmostvalue_text, bpchar_rhs_is_varlena, NULL, bpchar_cmp_fns)
     743             : 
     744             : static Datum
     745           8 : leftmostvalue_char(void)
     746             : {
     747           8 :     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          80 : 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          80 : GIN_SUPPORT(bytea, leftmostvalue_text, bytea_rhs_is_varlena, NULL, bytea_cmp_fns)
     765             : 
     766             : static Datum
     767           8 : leftmostvalue_bit(void)
     768             : {
     769           8 :     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          80 : GIN_SUPPORT(bit, leftmostvalue_bit, bit_rhs_is_varlena, NULL, bit_cmp_fns)
     782             : 
     783             : static Datum
     784           8 : leftmostvalue_varbit(void)
     785             : {
     786           8 :     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          80 : 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           6 : PG_FUNCTION_INFO_V1(gin_numeric_cmp);
     811             : 
     812             : Datum
     813          86 : gin_numeric_cmp(PG_FUNCTION_ARGS)
     814             : {
     815          86 :     Numeric     a = (Numeric) PG_GETARG_POINTER(0);
     816          86 :     Numeric     b = (Numeric) PG_GETARG_POINTER(1);
     817          86 :     int         res = 0;
     818             : 
     819          86 :     if (NUMERIC_IS_LEFTMOST(a))
     820             :     {
     821          12 :         res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
     822             :     }
     823          74 :     else if (NUMERIC_IS_LEFTMOST(b))
     824             :     {
     825           0 :         res = 1;
     826             :     }
     827             :     else
     828             :     {
     829          74 :         res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
     830             :                                                 NumericGetDatum(a),
     831             :                                                 NumericGetDatum(b)));
     832             :     }
     833             : 
     834          86 :     PG_RETURN_INT32(res);
     835             : }
     836             : 
     837             : static Datum
     838           8 : leftmostvalue_numeric(void)
     839             : {
     840           8 :     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          80 : 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           6 : PG_FUNCTION_INFO_V1(gin_enum_cmp);
     864             : 
     865             : Datum
     866      400104 : gin_enum_cmp(PG_FUNCTION_ARGS)
     867             : {
     868      400104 :     Oid         a = PG_GETARG_OID(0);
     869      400104 :     Oid         b = PG_GETARG_OID(1);
     870      400104 :     int         res = 0;
     871             : 
     872      400104 :     if (ENUM_IS_LEFTMOST(a))
     873             :     {
     874          12 :         res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
     875             :     }
     876      400092 :     else if (ENUM_IS_LEFTMOST(b))
     877             :     {
     878           0 :         res = 1;
     879             :     }
     880             :     else
     881             :     {
     882      400092 :         res = DatumGetInt32(CallerFInfoFunctionCall2(enum_cmp,
     883             :                                                      fcinfo->flinfo,
     884             :                                                      PG_GET_COLLATION(),
     885             :                                                      ObjectIdGetDatum(a),
     886             :                                                      ObjectIdGetDatum(b)));
     887             :     }
     888             : 
     889      400104 :     PG_RETURN_INT32(res);
     890             : }
     891             : 
     892             : static Datum
     893           8 : leftmostvalue_enum(void)
     894             : {
     895           8 :     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      200090 : GIN_SUPPORT(anyenum, leftmostvalue_enum, enum_rhs_is_varlena, NULL, enum_cmp_fns)
     905             : 
     906             : static Datum
     907          12 : leftmostvalue_uuid(void)
     908             : {
     909             :     /*
     910             :      * palloc0 will create the UUID with all zeroes:
     911             :      * "00000000-0000-0000-0000-000000000000"
     912             :      */
     913          12 :     pg_uuid_t  *retval = palloc0_object(pg_uuid_t);
     914             : 
     915          12 :     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          90 : GIN_SUPPORT(uuid, leftmostvalue_uuid, uuid_rhs_is_varlena, NULL, uuid_cmp_fns)
     925             : 
     926             : static Datum
     927          26 : leftmostvalue_name(void)
     928             : {
     929          26 :     NameData   *result = (NameData *) palloc0(NAMEDATALEN);
     930             : 
     931          26 :     return NameGetDatum(result);
     932             : }
     933             : 
     934             : static Datum
     935          12 : cvt_text_name(Datum input)
     936             : {
     937          12 :     text       *val = DatumGetTextPP(input);
     938          12 :     NameData   *result = (NameData *) palloc0(NAMEDATALEN);
     939          12 :     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          12 :     if (len >= NAMEDATALEN)
     948           0 :         len = pg_mbcliplen(VARDATA_ANY(val), len, NAMEDATALEN - 1);
     949             : 
     950          12 :     memcpy(NameStr(*result), VARDATA_ANY(val), len);
     951             : 
     952          12 :     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         160 : GIN_SUPPORT(name, leftmostvalue_name, name_rhs_is_varlena, name_cvt_fns, name_cmp_fns)
     965             : 
     966             : static Datum
     967          20 : leftmostvalue_bool(void)
     968             : {
     969          20 :     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         100 : GIN_SUPPORT(bool, leftmostvalue_bool, bool_rhs_is_varlena, NULL, bool_cmp_fns)

Generated by: LCOV version 1.16