LCOV - code coverage report
Current view: top level - contrib/btree_gin - btree_gin.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 97.4 % 266 259
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 218 218
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 76.1 % 46 35

             Branch data     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