LCOV - code coverage report
Current view: top level - contrib/btree_gin - btree_gin.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 179 192 93.2 %
Date: 2020-06-05 19:06:29 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         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         380 :         case BTEqualStrategyNumber:
      94         380 :             entries[0] = datum;
      95         380 :             break;
      96           0 :         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(data->typecmp,
     118             :                                                  fcinfo->flinfo,
     119             :                                                  PG_GET_COLLATION(),
     120             :                                                  (data->strategy == BTLessStrategyNumber ||
     121             :                                                   data->strategy == BTLessEqualStrategyNumber)
     122             :                                                  ? data->datum : a,
     123             :                                                  b));
     124             : 
     125         852 :     switch (data->strategy)
     126             :     {
     127         222 :         case BTLessStrategyNumber:
     128             :             /* If original datum > indexed one then return match */
     129         222 :             if (cmp > 0)
     130         164 :                 res = 0;
     131             :             else
     132          58 :                 res = 1;
     133         222 :             break;
     134         278 :         case BTLessEqualStrategyNumber:
     135             :             /* The same except equality */
     136         278 :             if (cmp >= 0)
     137         222 :                 res = 0;
     138             :             else
     139          56 :                 res = 1;
     140         278 :             break;
     141           0 :         case BTEqualStrategyNumber:
     142           0 :             if (cmp != 0)
     143           0 :                 res = 1;
     144             :             else
     145           0 :                 res = 0;
     146           0 :             break;
     147         176 :         case BTGreaterEqualStrategyNumber:
     148             :             /* If original datum <= indexed one then return match */
     149         176 :             if (cmp <= 0)
     150         176 :                 res = 0;
     151             :             else
     152           0 :                 res = 1;
     153         176 :             break;
     154         176 :         case BTGreaterStrategyNumber:
     155             :             /* If original datum <= indexed one then return match */
     156             :             /* If original datum == indexed one then continue scan */
     157         176 :             if (cmp < 0)
     158         116 :                 res = 0;
     159          60 :             else if (cmp == 0)
     160          60 :                 res = -1;
     161             :             else
     162           0 :                 res = 1;
     163         176 :             break;
     164           0 :         default:
     165           0 :             elog(ERROR, "unrecognized strategy number: %d",
     166             :                  data->strategy);
     167             :             res = 0;
     168             :     }
     169             : 
     170         852 :     PG_RETURN_INT32(res);
     171             : }
     172             : 
     173          60 : PG_FUNCTION_INFO_V1(gin_btree_consistent);
     174             : Datum
     175         748 : gin_btree_consistent(PG_FUNCTION_ARGS)
     176             : {
     177         748 :     bool       *recheck = (bool *) PG_GETARG_POINTER(5);
     178             : 
     179         748 :     *recheck = false;
     180         748 :     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 :     v->time = DT_NOBEGIN;
     310           8 :     v->day = 0;
     311           8 :     v->month = 0;
     312           8 :     return IntervalPGetDatum(v);
     313             : }
     314             : 
     315          74 : GIN_SUPPORT(interval, false, leftmostvalue_interval, interval_cmp)
     316             : 
     317             : static Datum
     318           8 : leftmostvalue_macaddr(void)
     319             : {
     320           8 :     macaddr    *v = palloc0(sizeof(macaddr));
     321             : 
     322           8 :     return MacaddrPGetDatum(v);
     323             : }
     324             : 
     325          74 : GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp)
     326             : 
     327             : static Datum
     328           8 : leftmostvalue_macaddr8(void)
     329             : {
     330           8 :     macaddr8   *v = palloc0(sizeof(macaddr8));
     331             : 
     332           8 :     return Macaddr8PGetDatum(v);
     333             : }
     334             : 
     335          74 : GIN_SUPPORT(macaddr8, false, leftmostvalue_macaddr8, macaddr8_cmp)
     336             : 
     337             : static Datum
     338          16 : leftmostvalue_inet(void)
     339             : {
     340          16 :     return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
     341             : }
     342             : 
     343          74 : GIN_SUPPORT(inet, true, leftmostvalue_inet, network_cmp)
     344             : 
     345          74 : GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
     346             : 
     347             : static Datum
     348          36 : leftmostvalue_text(void)
     349             : {
     350          36 :     return PointerGetDatum(cstring_to_text_with_len("", 0));
     351             : }
     352             : 
     353         142 : GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
     354             : 
     355          92 : GIN_SUPPORT(bpchar, true, leftmostvalue_text, bpcharcmp)
     356             : 
     357             : static Datum
     358           8 : leftmostvalue_char(void)
     359             : {
     360           8 :     return CharGetDatum(SCHAR_MIN);
     361             : }
     362             : 
     363          56 : GIN_SUPPORT(char, false, leftmostvalue_char, btcharcmp)
     364             : 
     365          74 : GIN_SUPPORT(bytea, true, leftmostvalue_text, byteacmp)
     366             : 
     367             : static Datum
     368           8 : leftmostvalue_bit(void)
     369             : {
     370           8 :     return DirectFunctionCall3(bit_in,
     371             :                                CStringGetDatum(""),
     372             :                                ObjectIdGetDatum(0),
     373             :                                Int32GetDatum(-1));
     374             : }
     375             : 
     376          74 : GIN_SUPPORT(bit, true, leftmostvalue_bit, bitcmp)
     377             : 
     378             : static Datum
     379           8 : leftmostvalue_varbit(void)
     380             : {
     381           8 :     return DirectFunctionCall3(varbit_in,
     382             :                                CStringGetDatum(""),
     383             :                                ObjectIdGetDatum(0),
     384             :                                Int32GetDatum(-1));
     385             : }
     386             : 
     387          74 : GIN_SUPPORT(varbit, true, leftmostvalue_varbit, bitcmp)
     388             : 
     389             : /*
     390             :  * Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
     391             :  * (*not* a SQL NULL) to represent that.  We can get away with that because
     392             :  * the value returned by our leftmostvalue function will never be stored in
     393             :  * the index nor passed to anything except our compare and prefix-comparison
     394             :  * functions.  The same trick could be used for other pass-by-reference types.
     395             :  */
     396             : 
     397             : #define NUMERIC_IS_LEFTMOST(x)  ((x) == NULL)
     398             : 
     399           4 : PG_FUNCTION_INFO_V1(gin_numeric_cmp);
     400             : 
     401             : Datum
     402          86 : gin_numeric_cmp(PG_FUNCTION_ARGS)
     403             : {
     404          86 :     Numeric     a = (Numeric) PG_GETARG_POINTER(0);
     405          86 :     Numeric     b = (Numeric) PG_GETARG_POINTER(1);
     406          86 :     int         res = 0;
     407             : 
     408          86 :     if (NUMERIC_IS_LEFTMOST(a))
     409             :     {
     410          12 :         res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
     411             :     }
     412          74 :     else if (NUMERIC_IS_LEFTMOST(b))
     413             :     {
     414           0 :         res = 1;
     415             :     }
     416             :     else
     417             :     {
     418          74 :         res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
     419             :                                                 NumericGetDatum(a),
     420             :                                                 NumericGetDatum(b)));
     421             :     }
     422             : 
     423          86 :     PG_RETURN_INT32(res);
     424             : }
     425             : 
     426             : static Datum
     427           8 : leftmostvalue_numeric(void)
     428             : {
     429           8 :     return PointerGetDatum(NULL);
     430             : }
     431             : 
     432          74 : GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp)
     433             : 
     434             : /*
     435             :  * Use a similar trick to that used for numeric for enums, since we don't
     436             :  * actually know the leftmost value of any enum without knowing the concrete
     437             :  * type, so we use a dummy leftmost value of InvalidOid.
     438             :  *
     439             :  * Note that we use CallerFInfoFunctionCall2 here so that enum_cmp
     440             :  * gets a valid fn_extra to work with. Unlike most other type comparison
     441             :  * routines it needs it, so we can't use DirectFunctionCall2.
     442             :  */
     443             : 
     444             : #define ENUM_IS_LEFTMOST(x) ((x) == InvalidOid)
     445             : 
     446           4 : PG_FUNCTION_INFO_V1(gin_enum_cmp);
     447             : 
     448             : Datum
     449      400104 : gin_enum_cmp(PG_FUNCTION_ARGS)
     450             : {
     451      400104 :     Oid         a = PG_GETARG_OID(0);
     452      400104 :     Oid         b = PG_GETARG_OID(1);
     453      400104 :     int         res = 0;
     454             : 
     455      400104 :     if (ENUM_IS_LEFTMOST(a))
     456             :     {
     457          12 :         res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
     458             :     }
     459      400092 :     else if (ENUM_IS_LEFTMOST(b))
     460             :     {
     461           0 :         res = 1;
     462             :     }
     463             :     else
     464             :     {
     465      400092 :         res = DatumGetInt32(CallerFInfoFunctionCall2(enum_cmp,
     466             :                                                      fcinfo->flinfo,
     467             :                                                      PG_GET_COLLATION(),
     468             :                                                      ObjectIdGetDatum(a),
     469             :                                                      ObjectIdGetDatum(b)));
     470             :     }
     471             : 
     472      400104 :     PG_RETURN_INT32(res);
     473             : }
     474             : 
     475             : static Datum
     476           8 : leftmostvalue_enum(void)
     477             : {
     478           8 :     return ObjectIdGetDatum(InvalidOid);
     479             : }
     480             : 
     481      200084 : GIN_SUPPORT(anyenum, false, leftmostvalue_enum, gin_enum_cmp)
     482             : 
     483             : static Datum
     484          12 : leftmostvalue_uuid(void)
     485             : {
     486             :     /*
     487             :      * palloc0 will create the UUID with all zeroes:
     488             :      * "00000000-0000-0000-0000-000000000000"
     489             :      */
     490          12 :     pg_uuid_t  *retval = (pg_uuid_t *) palloc0(sizeof(pg_uuid_t));
     491             : 
     492          12 :     return UUIDPGetDatum(retval);
     493             : }
     494             : 
     495          84 : GIN_SUPPORT(uuid, false, leftmostvalue_uuid, uuid_cmp)
     496             : 
     497             : static Datum
     498          12 : leftmostvalue_name(void)
     499             : {
     500          12 :     NameData   *result = (NameData *) palloc0(NAMEDATALEN);
     501             : 
     502          12 :     return NameGetDatum(result);
     503             : }
     504             : 
     505          84 : GIN_SUPPORT(name, false, leftmostvalue_name, btnamecmp)
     506             : 
     507             : static Datum
     508          20 : leftmostvalue_bool(void)
     509             : {
     510          20 :     return BoolGetDatum(false);
     511             : }
     512             : 
     513          82 : GIN_SUPPORT(bool, false, leftmostvalue_bool, btboolcmp)

Generated by: LCOV version 1.13