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

Generated by: LCOV version 1.14