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

Generated by: LCOV version 1.13