LCOV - code coverage report
Current view: top level - contrib/btree_gin - btree_gin.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 179 192 93.2 %
Date: 2024-04-26 21:11:37 Functions: 202 202 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 "utils/builtins.h"
      10             : #include "utils/bytea.h"
      11             : #include "utils/cash.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 "utils/varbit.h"
      19             : 
      20          60 : PG_MODULE_MAGIC;
      21             : 
      22             : typedef struct QueryInfo
      23             : {
      24             :     StrategyNumber strategy;
      25             :     Datum       datum;
      26             :     bool        is_varlena;
      27             :     Datum       (*typecmp) (FunctionCallInfo);
      28             : } QueryInfo;
      29             : 
      30             : /*** GIN support functions shared by all datatypes ***/
      31             : 
      32             : static Datum
      33      200352 : gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
      34             : {
      35      200352 :     Datum       datum = PG_GETARG_DATUM(0);
      36      200352 :     int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
      37      200352 :     Datum      *entries = (Datum *) palloc(sizeof(Datum));
      38             : 
      39      200352 :     if (is_varlena)
      40         112 :         datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
      41      200352 :     entries[0] = datum;
      42      200352 :     *nentries = 1;
      43             : 
      44      200352 :     PG_RETURN_POINTER(entries);
      45             : }
      46             : 
      47             : /*
      48             :  * For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
      49             :  * BTEqualStrategyNumber we want to start the index scan at the
      50             :  * supplied query datum, and work forward. For BTLessStrategyNumber
      51             :  * and BTLessEqualStrategyNumber, we need to start at the leftmost
      52             :  * key, and work forward until the supplied query datum (which must be
      53             :  * sent along inside the QueryInfo structure).
      54             :  */
      55             : static Datum
      56         648 : gin_btree_extract_query(FunctionCallInfo fcinfo,
      57             :                         bool is_varlena,
      58             :                         Datum (*leftmostvalue) (void),
      59             :                         Datum (*typecmp) (FunctionCallInfo))
      60             : {
      61         648 :     Datum       datum = PG_GETARG_DATUM(0);
      62         648 :     int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
      63         648 :     StrategyNumber strategy = PG_GETARG_UINT16(2);
      64         648 :     bool      **partialmatch = (bool **) PG_GETARG_POINTER(3);
      65         648 :     Pointer   **extra_data = (Pointer **) PG_GETARG_POINTER(4);
      66         648 :     Datum      *entries = (Datum *) palloc(sizeof(Datum));
      67         648 :     QueryInfo  *data = (QueryInfo *) palloc(sizeof(QueryInfo));
      68             :     bool       *ptr_partialmatch;
      69             : 
      70         648 :     *nentries = 1;
      71         648 :     ptr_partialmatch = *partialmatch = (bool *) palloc(sizeof(bool));
      72         648 :     *ptr_partialmatch = false;
      73         648 :     if (is_varlena)
      74         194 :         datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
      75         648 :     data->strategy = strategy;
      76         648 :     data->datum = datum;
      77         648 :     data->is_varlena = is_varlena;
      78         648 :     data->typecmp = typecmp;
      79         648 :     *extra_data = (Pointer *) palloc(sizeof(Pointer));
      80         648 :     **extra_data = (Pointer) data;
      81             : 
      82         648 :     switch (strategy)
      83             :     {
      84         256 :         case BTLessStrategyNumber:
      85             :         case BTLessEqualStrategyNumber:
      86         256 :             entries[0] = leftmostvalue();
      87         256 :             *ptr_partialmatch = true;
      88         256 :             break;
      89         258 :         case BTGreaterEqualStrategyNumber:
      90             :         case BTGreaterStrategyNumber:
      91         258 :             *ptr_partialmatch = true;
      92             :             /* FALLTHROUGH */
      93         392 :         case BTEqualStrategyNumber:
      94         392 :             entries[0] = datum;
      95         392 :             break;
      96           0 :         default:
      97           0 :             elog(ERROR, "unrecognized strategy number: %d", strategy);
      98             :     }
      99             : 
     100         648 :     PG_RETURN_POINTER(entries);
     101             : }
     102             : 
     103             : /*
     104             :  * Datum a is a value from extract_query method and for BTLess*
     105             :  * strategy it is a left-most value.  So, use original datum from QueryInfo
     106             :  * to decide to stop scanning or not.  Datum b is always from index.
     107             :  */
     108             : static Datum
     109         878 : gin_btree_compare_prefix(FunctionCallInfo fcinfo)
     110             : {
     111         878 :     Datum       a = PG_GETARG_DATUM(0);
     112         878 :     Datum       b = PG_GETARG_DATUM(1);
     113         878 :     QueryInfo  *data = (QueryInfo *) PG_GETARG_POINTER(3);
     114             :     int32       res,
     115             :                 cmp;
     116             : 
     117         878 :     cmp = DatumGetInt32(CallerFInfoFunctionCall2(data->typecmp,
     118             :                                                  fcinfo->flinfo,
     119             :                                                  PG_GET_COLLATION(),
     120         878 :                                                  (data->strategy == BTLessStrategyNumber ||
     121         646 :                                                   data->strategy == BTLessEqualStrategyNumber)
     122             :                                                  ? data->datum : a,
     123             :                                                  b));
     124             : 
     125         878 :     switch (data->strategy)
     126             :     {
     127         232 :         case BTLessStrategyNumber:
     128             :             /* If original datum > indexed one then return match */
     129         232 :             if (cmp > 0)
     130         172 :                 res = 0;
     131             :             else
     132          60 :                 res = 1;
     133         232 :             break;
     134         290 :         case BTLessEqualStrategyNumber:
     135             :             /* The same except equality */
     136         290 :             if (cmp >= 0)
     137         232 :                 res = 0;
     138             :             else
     139          58 :                 res = 1;
     140         290 :             break;
     141           0 :         case BTEqualStrategyNumber:
     142           0 :             if (cmp != 0)
     143           0 :                 res = 1;
     144             :             else
     145           0 :                 res = 0;
     146           0 :             break;
     147         178 :         case BTGreaterEqualStrategyNumber:
     148             :             /* If original datum <= indexed one then return match */
     149         178 :             if (cmp <= 0)
     150         178 :                 res = 0;
     151             :             else
     152           0 :                 res = 1;
     153         178 :             break;
     154         178 :         case BTGreaterStrategyNumber:
     155             :             /* If original datum <= indexed one then return match */
     156             :             /* If original datum == indexed one then continue scan */
     157         178 :             if (cmp < 0)
     158         118 :                 res = 0;
     159          60 :             else if (cmp == 0)
     160          60 :                 res = -1;
     161             :             else
     162           0 :                 res = 1;
     163         178 :             break;
     164           0 :         default:
     165           0 :             elog(ERROR, "unrecognized strategy number: %d",
     166             :                  data->strategy);
     167             :             res = 0;
     168             :     }
     169             : 
     170         878 :     PG_RETURN_INT32(res);
     171             : }
     172             : 
     173          60 : PG_FUNCTION_INFO_V1(gin_btree_consistent);
     174             : Datum
     175         774 : gin_btree_consistent(PG_FUNCTION_ARGS)
     176             : {
     177         774 :     bool       *recheck = (bool *) PG_GETARG_POINTER(5);
     178             : 
     179         774 :     *recheck = false;
     180         774 :     PG_RETURN_BOOL(true);
     181             : }
     182             : 
     183             : /*** GIN_SUPPORT macro defines the datatype specific functions ***/
     184             : 
     185             : #define GIN_SUPPORT(type, is_varlena, leftmostvalue, typecmp)               \
     186             : PG_FUNCTION_INFO_V1(gin_extract_value_##type);                              \
     187             : Datum                                                                       \
     188             : gin_extract_value_##type(PG_FUNCTION_ARGS)                                  \
     189             : {                                                                           \
     190             :     return gin_btree_extract_value(fcinfo, is_varlena);                     \
     191             : }   \
     192             : PG_FUNCTION_INFO_V1(gin_extract_query_##type);                              \
     193             : Datum                                                                       \
     194             : gin_extract_query_##type(PG_FUNCTION_ARGS)                                  \
     195             : {                                                                           \
     196             :     return gin_btree_extract_query(fcinfo,                                  \
     197             :                                    is_varlena, leftmostvalue, typecmp);     \
     198             : }   \
     199             : PG_FUNCTION_INFO_V1(gin_compare_prefix_##type);                             \
     200             : Datum                                                                       \
     201             : gin_compare_prefix_##type(PG_FUNCTION_ARGS)                                 \
     202             : {                                                                           \
     203             :     return gin_btree_compare_prefix(fcinfo);                                \
     204             : }
     205             : 
     206             : 
     207             : /*** Datatype specifications ***/
     208             : 
     209             : static Datum
     210           8 : leftmostvalue_int2(void)
     211             : {
     212           8 :     return Int16GetDatum(SHRT_MIN);
     213             : }
     214             : 
     215          74 : GIN_SUPPORT(int2, false, leftmostvalue_int2, btint2cmp)
     216             : 
     217             : static Datum
     218           8 : leftmostvalue_int4(void)
     219             : {
     220           8 :     return Int32GetDatum(INT_MIN);
     221             : }
     222             : 
     223          74 : GIN_SUPPORT(int4, false, leftmostvalue_int4, btint4cmp)
     224             : 
     225             : static Datum
     226           8 : leftmostvalue_int8(void)
     227             : {
     228           8 :     return Int64GetDatum(PG_INT64_MIN);
     229             : }
     230             : 
     231          74 : GIN_SUPPORT(int8, false, leftmostvalue_int8, btint8cmp)
     232             : 
     233             : static Datum
     234           8 : leftmostvalue_float4(void)
     235             : {
     236           8 :     return Float4GetDatum(-get_float4_infinity());
     237             : }
     238             : 
     239          74 : GIN_SUPPORT(float4, false, leftmostvalue_float4, btfloat4cmp)
     240             : 
     241             : static Datum
     242           8 : leftmostvalue_float8(void)
     243             : {
     244           8 :     return Float8GetDatum(-get_float8_infinity());
     245             : }
     246             : 
     247          74 : GIN_SUPPORT(float8, false, leftmostvalue_float8, btfloat8cmp)
     248             : 
     249             : static Datum
     250           8 : leftmostvalue_money(void)
     251             : {
     252           8 :     return Int64GetDatum(PG_INT64_MIN);
     253             : }
     254             : 
     255          74 : GIN_SUPPORT(money, false, leftmostvalue_money, cash_cmp)
     256             : 
     257             : static Datum
     258           8 : leftmostvalue_oid(void)
     259             : {
     260           8 :     return ObjectIdGetDatum(0);
     261             : }
     262             : 
     263          74 : GIN_SUPPORT(oid, false, leftmostvalue_oid, btoidcmp)
     264             : 
     265             : static Datum
     266          16 : leftmostvalue_timestamp(void)
     267             : {
     268          16 :     return TimestampGetDatum(DT_NOBEGIN);
     269             : }
     270             : 
     271          74 : GIN_SUPPORT(timestamp, false, leftmostvalue_timestamp, timestamp_cmp)
     272             : 
     273          74 : GIN_SUPPORT(timestamptz, false, leftmostvalue_timestamp, timestamp_cmp)
     274             : 
     275             : static Datum
     276           8 : leftmostvalue_time(void)
     277             : {
     278           8 :     return TimeADTGetDatum(0);
     279             : }
     280             : 
     281          74 : GIN_SUPPORT(time, false, leftmostvalue_time, time_cmp)
     282             : 
     283             : static Datum
     284           8 : leftmostvalue_timetz(void)
     285             : {
     286           8 :     TimeTzADT  *v = palloc(sizeof(TimeTzADT));
     287             : 
     288           8 :     v->time = 0;
     289           8 :     v->zone = -24 * 3600;        /* XXX is that true? */
     290             : 
     291           8 :     return TimeTzADTPGetDatum(v);
     292             : }
     293             : 
     294          74 : GIN_SUPPORT(timetz, false, leftmostvalue_timetz, timetz_cmp)
     295             : 
     296             : static Datum
     297           8 : leftmostvalue_date(void)
     298             : {
     299           8 :     return DateADTGetDatum(DATEVAL_NOBEGIN);
     300             : }
     301             : 
     302          74 : GIN_SUPPORT(date, false, leftmostvalue_date, date_cmp)
     303             : 
     304             : static Datum
     305           8 : leftmostvalue_interval(void)
     306             : {
     307           8 :     Interval   *v = palloc(sizeof(Interval));
     308             : 
     309           8 :     INTERVAL_NOBEGIN(v);
     310             : 
     311           8 :     return IntervalPGetDatum(v);
     312             : }
     313             : 
     314          86 : GIN_SUPPORT(interval, false, leftmostvalue_interval, interval_cmp)
     315             : 
     316             : static Datum
     317           8 : leftmostvalue_macaddr(void)
     318             : {
     319           8 :     macaddr    *v = palloc0(sizeof(macaddr));
     320             : 
     321           8 :     return MacaddrPGetDatum(v);
     322             : }
     323             : 
     324          74 : GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp)
     325             : 
     326             : static Datum
     327           8 : leftmostvalue_macaddr8(void)
     328             : {
     329           8 :     macaddr8   *v = palloc0(sizeof(macaddr8));
     330             : 
     331           8 :     return Macaddr8PGetDatum(v);
     332             : }
     333             : 
     334          74 : GIN_SUPPORT(macaddr8, false, leftmostvalue_macaddr8, macaddr8_cmp)
     335             : 
     336             : static Datum
     337          16 : leftmostvalue_inet(void)
     338             : {
     339          16 :     return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
     340             : }
     341             : 
     342          74 : GIN_SUPPORT(inet, true, leftmostvalue_inet, network_cmp)
     343             : 
     344          74 : GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
     345             : 
     346             : static Datum
     347          36 : leftmostvalue_text(void)
     348             : {
     349          36 :     return PointerGetDatum(cstring_to_text_with_len("", 0));
     350             : }
     351             : 
     352         142 : GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
     353             : 
     354          92 : GIN_SUPPORT(bpchar, true, leftmostvalue_text, bpcharcmp)
     355             : 
     356             : static Datum
     357           8 : leftmostvalue_char(void)
     358             : {
     359           8 :     return CharGetDatum(0);
     360             : }
     361             : 
     362          74 : GIN_SUPPORT(char, false, leftmostvalue_char, btcharcmp)
     363             : 
     364          74 : GIN_SUPPORT(bytea, true, leftmostvalue_text, byteacmp)
     365             : 
     366             : static Datum
     367           8 : leftmostvalue_bit(void)
     368             : {
     369           8 :     return DirectFunctionCall3(bit_in,
     370             :                                CStringGetDatum(""),
     371             :                                ObjectIdGetDatum(0),
     372             :                                Int32GetDatum(-1));
     373             : }
     374             : 
     375          74 : GIN_SUPPORT(bit, true, leftmostvalue_bit, bitcmp)
     376             : 
     377             : static Datum
     378           8 : leftmostvalue_varbit(void)
     379             : {
     380           8 :     return DirectFunctionCall3(varbit_in,
     381             :                                CStringGetDatum(""),
     382             :                                ObjectIdGetDatum(0),
     383             :                                Int32GetDatum(-1));
     384             : }
     385             : 
     386          74 : GIN_SUPPORT(varbit, true, leftmostvalue_varbit, bitcmp)
     387             : 
     388             : /*
     389             :  * Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
     390             :  * (*not* a SQL NULL) to represent that.  We can get away with that because
     391             :  * the value returned by our leftmostvalue function will never be stored in
     392             :  * the index nor passed to anything except our compare and prefix-comparison
     393             :  * functions.  The same trick could be used for other pass-by-reference types.
     394             :  */
     395             : 
     396             : #define NUMERIC_IS_LEFTMOST(x)  ((x) == NULL)
     397             : 
     398           4 : PG_FUNCTION_INFO_V1(gin_numeric_cmp);
     399             : 
     400             : Datum
     401          86 : gin_numeric_cmp(PG_FUNCTION_ARGS)
     402             : {
     403          86 :     Numeric     a = (Numeric) PG_GETARG_POINTER(0);
     404          86 :     Numeric     b = (Numeric) PG_GETARG_POINTER(1);
     405          86 :     int         res = 0;
     406             : 
     407          86 :     if (NUMERIC_IS_LEFTMOST(a))
     408             :     {
     409          12 :         res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
     410             :     }
     411          74 :     else if (NUMERIC_IS_LEFTMOST(b))
     412             :     {
     413           0 :         res = 1;
     414             :     }
     415             :     else
     416             :     {
     417          74 :         res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
     418             :                                                 NumericGetDatum(a),
     419             :                                                 NumericGetDatum(b)));
     420             :     }
     421             : 
     422          86 :     PG_RETURN_INT32(res);
     423             : }
     424             : 
     425             : static Datum
     426           8 : leftmostvalue_numeric(void)
     427             : {
     428           8 :     return PointerGetDatum(NULL);
     429             : }
     430             : 
     431          74 : GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp)
     432             : 
     433             : /*
     434             :  * Use a similar trick to that used for numeric for enums, since we don't
     435             :  * actually know the leftmost value of any enum without knowing the concrete
     436             :  * type, so we use a dummy leftmost value of InvalidOid.
     437             :  *
     438             :  * Note that we use CallerFInfoFunctionCall2 here so that enum_cmp
     439             :  * gets a valid fn_extra to work with. Unlike most other type comparison
     440             :  * routines it needs it, so we can't use DirectFunctionCall2.
     441             :  */
     442             : 
     443             : #define ENUM_IS_LEFTMOST(x) ((x) == InvalidOid)
     444             : 
     445           4 : PG_FUNCTION_INFO_V1(gin_enum_cmp);
     446             : 
     447             : Datum
     448      400104 : gin_enum_cmp(PG_FUNCTION_ARGS)
     449             : {
     450      400104 :     Oid         a = PG_GETARG_OID(0);
     451      400104 :     Oid         b = PG_GETARG_OID(1);
     452      400104 :     int         res = 0;
     453             : 
     454      400104 :     if (ENUM_IS_LEFTMOST(a))
     455             :     {
     456          12 :         res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
     457             :     }
     458      400092 :     else if (ENUM_IS_LEFTMOST(b))
     459             :     {
     460           0 :         res = 1;
     461             :     }
     462             :     else
     463             :     {
     464      400092 :         res = DatumGetInt32(CallerFInfoFunctionCall2(enum_cmp,
     465             :                                                      fcinfo->flinfo,
     466             :                                                      PG_GET_COLLATION(),
     467             :                                                      ObjectIdGetDatum(a),
     468             :                                                      ObjectIdGetDatum(b)));
     469             :     }
     470             : 
     471      400104 :     PG_RETURN_INT32(res);
     472             : }
     473             : 
     474             : static Datum
     475           8 : leftmostvalue_enum(void)
     476             : {
     477           8 :     return ObjectIdGetDatum(InvalidOid);
     478             : }
     479             : 
     480      200084 : GIN_SUPPORT(anyenum, false, leftmostvalue_enum, gin_enum_cmp)
     481             : 
     482             : static Datum
     483          12 : leftmostvalue_uuid(void)
     484             : {
     485             :     /*
     486             :      * palloc0 will create the UUID with all zeroes:
     487             :      * "00000000-0000-0000-0000-000000000000"
     488             :      */
     489          12 :     pg_uuid_t  *retval = (pg_uuid_t *) palloc0(sizeof(pg_uuid_t));
     490             : 
     491          12 :     return UUIDPGetDatum(retval);
     492             : }
     493             : 
     494          84 : GIN_SUPPORT(uuid, false, leftmostvalue_uuid, uuid_cmp)
     495             : 
     496             : static Datum
     497          12 : leftmostvalue_name(void)
     498             : {
     499          12 :     NameData   *result = (NameData *) palloc0(NAMEDATALEN);
     500             : 
     501          12 :     return NameGetDatum(result);
     502             : }
     503             : 
     504          84 : GIN_SUPPORT(name, false, leftmostvalue_name, btnamecmp)
     505             : 
     506             : static Datum
     507          20 : leftmostvalue_bool(void)
     508             : {
     509          20 :     return BoolGetDatum(false);
     510             : }
     511             : 
     512          94 : GIN_SUPPORT(bool, false, leftmostvalue_bool, btboolcmp)

Generated by: LCOV version 1.14