LCOV - code coverage report
Current view: top level - src/backend/access/brin - brin_inclusion.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 168 233 72.1 %
Date: 2020-05-29 01:06:25 Functions: 5 6 83.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * brin_inclusion.c
       3             :  *      Implementation of inclusion opclasses for BRIN
       4             :  *
       5             :  * This module provides framework BRIN support functions for the "inclusion"
       6             :  * operator classes.  A few SQL-level support functions are also required for
       7             :  * each opclass.
       8             :  *
       9             :  * The "inclusion" BRIN strategy is useful for types that support R-Tree
      10             :  * operations.  This implementation is a straight mapping of those operations
      11             :  * to the block-range nature of BRIN, with two exceptions: (a) we explicitly
      12             :  * support "empty" elements: at least with range types, we need to consider
      13             :  * emptiness separately from regular R-Tree strategies; and (b) we need to
      14             :  * consider "unmergeable" elements, that is, a set of elements for whose union
      15             :  * no representation exists.  The only case where that happens as of this
      16             :  * writing is the INET type, where IPv6 values cannot be merged with IPv4
      17             :  * values.
      18             :  *
      19             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
      20             :  * Portions Copyright (c) 1994, Regents of the University of California
      21             :  *
      22             :  * IDENTIFICATION
      23             :  *    src/backend/access/brin/brin_inclusion.c
      24             :  */
      25             : #include "postgres.h"
      26             : 
      27             : #include "access/brin_internal.h"
      28             : #include "access/brin_tuple.h"
      29             : #include "access/genam.h"
      30             : #include "access/skey.h"
      31             : #include "catalog/pg_amop.h"
      32             : #include "catalog/pg_type.h"
      33             : #include "utils/builtins.h"
      34             : #include "utils/datum.h"
      35             : #include "utils/lsyscache.h"
      36             : #include "utils/rel.h"
      37             : #include "utils/syscache.h"
      38             : 
      39             : 
      40             : /*
      41             :  * Additional SQL level support functions
      42             :  *
      43             :  * Procedure numbers must not use values reserved for BRIN itself; see
      44             :  * brin_internal.h.
      45             :  */
      46             : #define     INCLUSION_MAX_PROCNUMS  4   /* maximum support procs we need */
      47             : #define     PROCNUM_MERGE           11  /* required */
      48             : #define     PROCNUM_MERGEABLE       12  /* optional */
      49             : #define     PROCNUM_CONTAINS        13  /* optional */
      50             : #define     PROCNUM_EMPTY           14  /* optional */
      51             : 
      52             : 
      53             : /*
      54             :  * Subtract this from procnum to obtain index in InclusionOpaque arrays
      55             :  * (Must be equal to minimum of private procnums).
      56             :  */
      57             : #define     PROCNUM_BASE            11
      58             : 
      59             : /*-
      60             :  * The values stored in the bv_values arrays correspond to:
      61             :  *
      62             :  * INCLUSION_UNION
      63             :  *      the union of the values in the block range
      64             :  * INCLUSION_UNMERGEABLE
      65             :  *      whether the values in the block range cannot be merged
      66             :  *      (e.g. an IPv6 address amidst IPv4 addresses)
      67             :  * INCLUSION_CONTAINS_EMPTY
      68             :  *      whether an empty value is present in any tuple
      69             :  *      in the block range
      70             :  */
      71             : #define INCLUSION_UNION             0
      72             : #define INCLUSION_UNMERGEABLE       1
      73             : #define INCLUSION_CONTAINS_EMPTY    2
      74             : 
      75             : 
      76             : typedef struct InclusionOpaque
      77             : {
      78             :     FmgrInfo    extra_procinfos[INCLUSION_MAX_PROCNUMS];
      79             :     bool        extra_proc_missing[INCLUSION_MAX_PROCNUMS];
      80             :     Oid         cached_subtype;
      81             :     FmgrInfo    strategy_procinfos[RTMaxStrategyNumber];
      82             : } InclusionOpaque;
      83             : 
      84             : static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno,
      85             :                                         uint16 procnum);
      86             : static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno,
      87             :                                                  Oid subtype, uint16 strategynum);
      88             : 
      89             : 
      90             : /*
      91             :  * BRIN inclusion OpcInfo function
      92             :  */
      93             : Datum
      94        4072 : brin_inclusion_opcinfo(PG_FUNCTION_ARGS)
      95             : {
      96        4072 :     Oid         typoid = PG_GETARG_OID(0);
      97             :     BrinOpcInfo *result;
      98        4072 :     TypeCacheEntry *bool_typcache = lookup_type_cache(BOOLOID, 0);
      99             : 
     100             :     /*
     101             :      * All members of opaque are initialized lazily; both procinfo arrays
     102             :      * start out as non-initialized by having fn_oid be InvalidOid, and
     103             :      * "missing" to false, by zeroing here.  strategy_procinfos elements can
     104             :      * be invalidated when cached_subtype changes by zeroing fn_oid.
     105             :      * extra_procinfo entries are never invalidated, but if a lookup fails
     106             :      * (which is expected), extra_proc_missing is set to true, indicating not
     107             :      * to look it up again.
     108             :      */
     109        4072 :     result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque));
     110        4072 :     result->oi_nstored = 3;
     111        4072 :     result->oi_opaque = (InclusionOpaque *)
     112        4072 :         MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
     113             : 
     114             :     /* the union */
     115        4072 :     result->oi_typcache[INCLUSION_UNION] =
     116        4072 :         lookup_type_cache(typoid, 0);
     117             : 
     118             :     /* includes elements that are not mergeable */
     119        4072 :     result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache;
     120             : 
     121             :     /* includes the empty element */
     122        4072 :     result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache;
     123             : 
     124        4072 :     PG_RETURN_POINTER(result);
     125             : }
     126             : 
     127             : /*
     128             :  * BRIN inclusion add value function
     129             :  *
     130             :  * Examine the given index tuple (which contains partial status of a certain
     131             :  * page range) by comparing it to the given value that comes from another heap
     132             :  * tuple.  If the new value is outside the union specified by the existing
     133             :  * tuple values, update the index tuple and return true.  Otherwise, return
     134             :  * false and do not modify in this case.
     135             :  */
     136             : Datum
     137        5644 : brin_inclusion_add_value(PG_FUNCTION_ARGS)
     138             : {
     139        5644 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     140        5644 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     141        5644 :     Datum       newval = PG_GETARG_DATUM(2);
     142        5644 :     bool        isnull = PG_GETARG_BOOL(3);
     143        5644 :     Oid         colloid = PG_GET_COLLATION();
     144             :     FmgrInfo   *finfo;
     145             :     Datum       result;
     146        5644 :     bool        new = false;
     147             :     AttrNumber  attno;
     148             :     Form_pg_attribute attr;
     149             : 
     150             :     /*
     151             :      * If the new value is null, we record that we saw it if it's the first
     152             :      * one; otherwise, there's nothing to do.
     153             :      */
     154        5644 :     if (isnull)
     155             :     {
     156         132 :         if (column->bv_hasnulls)
     157          96 :             PG_RETURN_BOOL(false);
     158             : 
     159          36 :         column->bv_hasnulls = true;
     160          36 :         PG_RETURN_BOOL(true);
     161             :     }
     162             : 
     163        5512 :     attno = column->bv_attno;
     164        5512 :     attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     165             : 
     166             :     /*
     167             :      * If the recorded value is null, copy the new value (which we know to be
     168             :      * not null), and we're almost done.
     169             :      */
     170        5512 :     if (column->bv_allnulls)
     171             :     {
     172        3432 :         column->bv_values[INCLUSION_UNION] =
     173        1716 :             datumCopy(newval, attr->attbyval, attr->attlen);
     174        1716 :         column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false);
     175        1716 :         column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false);
     176        1716 :         column->bv_allnulls = false;
     177        1716 :         new = true;
     178             :     }
     179             : 
     180             :     /*
     181             :      * No need for further processing if the block range is marked as
     182             :      * containing unmergeable values.
     183             :      */
     184        5512 :     if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
     185         312 :         PG_RETURN_BOOL(false);
     186             : 
     187             :     /*
     188             :      * If the opclass supports the concept of empty values, test the passed
     189             :      * new value for emptiness; if it returns true, we need to set the
     190             :      * "contains empty" flag in the element (unless already set).
     191             :      */
     192        5200 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY);
     193        5200 :     if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval)))
     194             :     {
     195         752 :         if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]))
     196             :         {
     197         232 :             column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
     198         232 :             PG_RETURN_BOOL(true);
     199             :         }
     200             : 
     201         520 :         PG_RETURN_BOOL(false);
     202             :     }
     203             : 
     204        4448 :     if (new)
     205        1504 :         PG_RETURN_BOOL(true);
     206             : 
     207             :     /* Check if the new value is already contained. */
     208        2944 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS);
     209        5888 :     if (finfo != NULL &&
     210        2944 :         DatumGetBool(FunctionCall2Coll(finfo, colloid,
     211             :                                        column->bv_values[INCLUSION_UNION],
     212             :                                        newval)))
     213        2864 :         PG_RETURN_BOOL(false);
     214             : 
     215             :     /*
     216             :      * Check if the new value is mergeable to the existing union.  If it is
     217             :      * not, mark the value as containing unmergeable elements and get out.
     218             :      *
     219             :      * Note: at this point we could remove the value from the union, since
     220             :      * it's not going to be used any longer.  However, the BRIN framework
     221             :      * doesn't allow for the value not being present.  Improve someday.
     222             :      */
     223          80 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
     224         152 :     if (finfo != NULL &&
     225          72 :         !DatumGetBool(FunctionCall2Coll(finfo, colloid,
     226             :                                         column->bv_values[INCLUSION_UNION],
     227             :                                         newval)))
     228             :     {
     229          72 :         column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
     230          72 :         PG_RETURN_BOOL(true);
     231             :     }
     232             : 
     233             :     /* Finally, merge the new value to the existing union. */
     234           8 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
     235             :     Assert(finfo != NULL);
     236           8 :     result = FunctionCall2Coll(finfo, colloid,
     237           8 :                                column->bv_values[INCLUSION_UNION], newval);
     238           8 :     if (!attr->attbyval &&
     239           8 :         DatumGetPointer(result) != DatumGetPointer(column->bv_values[INCLUSION_UNION]))
     240             :     {
     241           8 :         pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION]));
     242             : 
     243           8 :         if (result == newval)
     244           4 :             result = datumCopy(result, attr->attbyval, attr->attlen);
     245             :     }
     246           8 :     column->bv_values[INCLUSION_UNION] = result;
     247             : 
     248           8 :     PG_RETURN_BOOL(true);
     249             : }
     250             : 
     251             : /*
     252             :  * BRIN inclusion consistent function
     253             :  *
     254             :  * All of the strategies are optional.
     255             :  */
     256             : Datum
     257       28400 : brin_inclusion_consistent(PG_FUNCTION_ARGS)
     258             : {
     259       28400 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     260       28400 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     261       28400 :     ScanKey     key = (ScanKey) PG_GETARG_POINTER(2);
     262       28400 :     Oid         colloid = PG_GET_COLLATION(),
     263             :                 subtype;
     264             :     Datum       unionval;
     265             :     AttrNumber  attno;
     266             :     Datum       query;
     267             :     FmgrInfo   *finfo;
     268             :     Datum       result;
     269             : 
     270             :     Assert(key->sk_attno == column->bv_attno);
     271             : 
     272             :     /* Handle IS NULL/IS NOT NULL tests. */
     273       28400 :     if (key->sk_flags & SK_ISNULL)
     274             :     {
     275           0 :         if (key->sk_flags & SK_SEARCHNULL)
     276             :         {
     277           0 :             if (column->bv_allnulls || column->bv_hasnulls)
     278           0 :                 PG_RETURN_BOOL(true);
     279           0 :             PG_RETURN_BOOL(false);
     280             :         }
     281             : 
     282             :         /*
     283             :          * For IS NOT NULL, we can only skip ranges that are known to have
     284             :          * only nulls.
     285             :          */
     286           0 :         if (key->sk_flags & SK_SEARCHNOTNULL)
     287           0 :             PG_RETURN_BOOL(!column->bv_allnulls);
     288             : 
     289             :         /*
     290             :          * Neither IS NULL nor IS NOT NULL was used; assume all indexable
     291             :          * operators are strict and return false.
     292             :          */
     293           0 :         PG_RETURN_BOOL(false);
     294             :     }
     295             : 
     296             :     /* If it is all nulls, it cannot possibly be consistent. */
     297       28400 :     if (column->bv_allnulls)
     298           0 :         PG_RETURN_BOOL(false);
     299             : 
     300             :     /* It has to be checked, if it contains elements that are not mergeable. */
     301       28400 :     if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
     302        1092 :         PG_RETURN_BOOL(true);
     303             : 
     304       27308 :     attno = key->sk_attno;
     305       27308 :     subtype = key->sk_subtype;
     306       27308 :     query = key->sk_argument;
     307       27308 :     unionval = column->bv_values[INCLUSION_UNION];
     308       27308 :     switch (key->sk_strategy)
     309             :     {
     310             :             /*
     311             :              * Placement strategies
     312             :              *
     313             :              * These are implemented by logically negating the result of the
     314             :              * converse placement operator; for this to work, the converse
     315             :              * operator must be part of the opclass.  An error will be thrown
     316             :              * by inclusion_get_strategy_procinfo() if the required strategy
     317             :              * is not part of the opclass.
     318             :              *
     319             :              * These all return false if either argument is empty, so there is
     320             :              * no need to check for empty elements.
     321             :              */
     322             : 
     323         800 :         case RTLeftStrategyNumber:
     324         800 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     325             :                                                     RTOverRightStrategyNumber);
     326         800 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     327         800 :             PG_RETURN_BOOL(!DatumGetBool(result));
     328             : 
     329         800 :         case RTOverLeftStrategyNumber:
     330         800 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     331             :                                                     RTRightStrategyNumber);
     332         800 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     333         800 :             PG_RETURN_BOOL(!DatumGetBool(result));
     334             : 
     335         800 :         case RTOverRightStrategyNumber:
     336         800 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     337             :                                                     RTLeftStrategyNumber);
     338         800 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     339         800 :             PG_RETURN_BOOL(!DatumGetBool(result));
     340             : 
     341         800 :         case RTRightStrategyNumber:
     342         800 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     343             :                                                     RTOverLeftStrategyNumber);
     344         800 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     345         800 :             PG_RETURN_BOOL(!DatumGetBool(result));
     346             : 
     347         400 :         case RTBelowStrategyNumber:
     348         400 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     349             :                                                     RTOverAboveStrategyNumber);
     350         400 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     351         400 :             PG_RETURN_BOOL(!DatumGetBool(result));
     352             : 
     353         400 :         case RTOverBelowStrategyNumber:
     354         400 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     355             :                                                     RTAboveStrategyNumber);
     356         400 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     357         400 :             PG_RETURN_BOOL(!DatumGetBool(result));
     358             : 
     359         400 :         case RTOverAboveStrategyNumber:
     360         400 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     361             :                                                     RTBelowStrategyNumber);
     362         400 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     363         400 :             PG_RETURN_BOOL(!DatumGetBool(result));
     364             : 
     365         400 :         case RTAboveStrategyNumber:
     366         400 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     367             :                                                     RTOverBelowStrategyNumber);
     368         400 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     369         400 :             PG_RETURN_BOOL(!DatumGetBool(result));
     370             : 
     371             :             /*
     372             :              * Overlap and contains strategies
     373             :              *
     374             :              * These strategies are simple enough that we can simply call the
     375             :              * operator and return its result.  Empty elements don't change
     376             :              * the result.
     377             :              */
     378             : 
     379       10240 :         case RTOverlapStrategyNumber:
     380             :         case RTContainsStrategyNumber:
     381             :         case RTOldContainsStrategyNumber:
     382             :         case RTContainsElemStrategyNumber:
     383             :         case RTSubStrategyNumber:
     384             :         case RTSubEqualStrategyNumber:
     385       10240 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     386       10240 :                                                     key->sk_strategy);
     387       10240 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     388       10240 :             PG_RETURN_DATUM(result);
     389             : 
     390             :             /*
     391             :              * Contained by strategies
     392             :              *
     393             :              * We cannot just call the original operator for the contained by
     394             :              * strategies because some elements can be contained even though
     395             :              * the union is not; instead we use the overlap operator.
     396             :              *
     397             :              * We check for empty elements separately as they are not merged
     398             :              * to the union but contained by everything.
     399             :              */
     400             : 
     401        5664 :         case RTContainedByStrategyNumber:
     402             :         case RTOldContainedByStrategyNumber:
     403             :         case RTSuperStrategyNumber:
     404             :         case RTSuperEqualStrategyNumber:
     405        5664 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     406             :                                                     RTOverlapStrategyNumber);
     407        5664 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     408        5664 :             if (DatumGetBool(result))
     409        3376 :                 PG_RETURN_BOOL(true);
     410             : 
     411        2288 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     412             : 
     413             :             /*
     414             :              * Adjacent strategy
     415             :              *
     416             :              * We test for overlap first but to be safe we need to call the
     417             :              * actual adjacent operator also.
     418             :              *
     419             :              * An empty element cannot be adjacent to any other, so there is
     420             :              * no need to check for it.
     421             :              */
     422             : 
     423           0 :         case RTAdjacentStrategyNumber:
     424           0 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     425             :                                                     RTOverlapStrategyNumber);
     426           0 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     427           0 :             if (DatumGetBool(result))
     428           0 :                 PG_RETURN_BOOL(true);
     429             : 
     430           0 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     431             :                                                     RTAdjacentStrategyNumber);
     432           0 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     433           0 :             PG_RETURN_DATUM(result);
     434             : 
     435             :             /*
     436             :              * Basic comparison strategies
     437             :              *
     438             :              * It is straightforward to support the equality strategies with
     439             :              * the contains operator.  Generally, inequality strategies do not
     440             :              * make much sense for the types which will be used with the
     441             :              * inclusion BRIN family of opclasses, but it is possible to
     442             :              * implement them with logical negation of the left-of and
     443             :              * right-of operators.
     444             :              *
     445             :              * NB: These strategies cannot be used with geometric datatypes
     446             :              * that use comparison of areas!  The only exception is the "same"
     447             :              * strategy.
     448             :              *
     449             :              * Empty elements are considered to be less than the others.  We
     450             :              * cannot use the empty support function to check the query is an
     451             :              * empty element, because the query can be another data type than
     452             :              * the empty support function argument.  So we will return true,
     453             :              * if there is a possibility that empty elements will change the
     454             :              * result.
     455             :              */
     456             : 
     457        1200 :         case RTLessStrategyNumber:
     458             :         case RTLessEqualStrategyNumber:
     459        1200 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     460             :                                                     RTRightStrategyNumber);
     461        1200 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     462        1200 :             if (!DatumGetBool(result))
     463        1000 :                 PG_RETURN_BOOL(true);
     464             : 
     465         200 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     466             : 
     467        3804 :         case RTSameStrategyNumber:
     468             :         case RTEqualStrategyNumber:
     469        3804 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     470             :                                                     RTContainsStrategyNumber);
     471        3804 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     472        3804 :             if (DatumGetBool(result))
     473         468 :                 PG_RETURN_BOOL(true);
     474             : 
     475        3336 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     476             : 
     477         800 :         case RTGreaterEqualStrategyNumber:
     478         800 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     479             :                                                     RTLeftStrategyNumber);
     480         800 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     481         800 :             if (!DatumGetBool(result))
     482         800 :                 PG_RETURN_BOOL(true);
     483             : 
     484           0 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     485             : 
     486         800 :         case RTGreaterStrategyNumber:
     487             :             /* no need to check for empty elements */
     488         800 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     489             :                                                     RTLeftStrategyNumber);
     490         800 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     491         800 :             PG_RETURN_BOOL(!DatumGetBool(result));
     492             : 
     493           0 :         default:
     494             :             /* shouldn't happen */
     495           0 :             elog(ERROR, "invalid strategy number %d", key->sk_strategy);
     496             :             PG_RETURN_BOOL(false);
     497             :     }
     498             : }
     499             : 
     500             : /*
     501             :  * BRIN inclusion union function
     502             :  *
     503             :  * Given two BrinValues, update the first of them as a union of the summary
     504             :  * values contained in both.  The second one is untouched.
     505             :  */
     506             : Datum
     507           0 : brin_inclusion_union(PG_FUNCTION_ARGS)
     508             : {
     509           0 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     510           0 :     BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
     511           0 :     BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
     512           0 :     Oid         colloid = PG_GET_COLLATION();
     513             :     AttrNumber  attno;
     514             :     Form_pg_attribute attr;
     515             :     FmgrInfo   *finfo;
     516             :     Datum       result;
     517             : 
     518             :     Assert(col_a->bv_attno == col_b->bv_attno);
     519             : 
     520             :     /* Adjust "hasnulls". */
     521           0 :     if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
     522           0 :         col_a->bv_hasnulls = true;
     523             : 
     524             :     /* If there are no values in B, there's nothing left to do. */
     525           0 :     if (col_b->bv_allnulls)
     526           0 :         PG_RETURN_VOID();
     527             : 
     528           0 :     attno = col_a->bv_attno;
     529           0 :     attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     530             : 
     531             :     /*
     532             :      * Adjust "allnulls".  If A doesn't have values, just copy the values from
     533             :      * B into A, and we're done.  We cannot run the operators in this case,
     534             :      * because values in A might contain garbage.  Note we already established
     535             :      * that B contains values.
     536             :      */
     537           0 :     if (col_a->bv_allnulls)
     538             :     {
     539           0 :         col_a->bv_allnulls = false;
     540           0 :         col_a->bv_values[INCLUSION_UNION] =
     541           0 :             datumCopy(col_b->bv_values[INCLUSION_UNION],
     542           0 :                       attr->attbyval, attr->attlen);
     543           0 :         col_a->bv_values[INCLUSION_UNMERGEABLE] =
     544           0 :             col_b->bv_values[INCLUSION_UNMERGEABLE];
     545           0 :         col_a->bv_values[INCLUSION_CONTAINS_EMPTY] =
     546           0 :             col_b->bv_values[INCLUSION_CONTAINS_EMPTY];
     547           0 :         PG_RETURN_VOID();
     548             :     }
     549             : 
     550             :     /* If B includes empty elements, mark A similarly, if needed. */
     551           0 :     if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&
     552           0 :         DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY]))
     553           0 :         col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
     554             : 
     555             :     /* Check if A includes elements that are not mergeable. */
     556           0 :     if (DatumGetBool(col_a->bv_values[INCLUSION_UNMERGEABLE]))
     557           0 :         PG_RETURN_VOID();
     558             : 
     559             :     /* If B includes elements that are not mergeable, mark A similarly. */
     560           0 :     if (DatumGetBool(col_b->bv_values[INCLUSION_UNMERGEABLE]))
     561             :     {
     562           0 :         col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
     563           0 :         PG_RETURN_VOID();
     564             :     }
     565             : 
     566             :     /* Check if A and B are mergeable; if not, mark A unmergeable. */
     567           0 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
     568           0 :     if (finfo != NULL &&
     569           0 :         !DatumGetBool(FunctionCall2Coll(finfo, colloid,
     570             :                                         col_a->bv_values[INCLUSION_UNION],
     571             :                                         col_b->bv_values[INCLUSION_UNION])))
     572             :     {
     573           0 :         col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
     574           0 :         PG_RETURN_VOID();
     575             :     }
     576             : 
     577             :     /* Finally, merge B to A. */
     578           0 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
     579             :     Assert(finfo != NULL);
     580           0 :     result = FunctionCall2Coll(finfo, colloid,
     581           0 :                                col_a->bv_values[INCLUSION_UNION],
     582           0 :                                col_b->bv_values[INCLUSION_UNION]);
     583           0 :     if (!attr->attbyval &&
     584           0 :         DatumGetPointer(result) != DatumGetPointer(col_a->bv_values[INCLUSION_UNION]))
     585             :     {
     586           0 :         pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION]));
     587             : 
     588           0 :         if (result == col_b->bv_values[INCLUSION_UNION])
     589           0 :             result = datumCopy(result, attr->attbyval, attr->attlen);
     590             :     }
     591           0 :     col_a->bv_values[INCLUSION_UNION] = result;
     592             : 
     593           0 :     PG_RETURN_VOID();
     594             : }
     595             : 
     596             : /*
     597             :  * Cache and return inclusion opclass support procedure
     598             :  *
     599             :  * Return the procedure corresponding to the given function support number
     600             :  * or null if it is not exists.
     601             :  */
     602             : static FmgrInfo *
     603        8232 : inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
     604             : {
     605             :     InclusionOpaque *opaque;
     606        8232 :     uint16      basenum = procnum - PROCNUM_BASE;
     607             : 
     608             :     /*
     609             :      * We cache these in the opaque struct, to avoid repetitive syscache
     610             :      * lookups.
     611             :      */
     612        8232 :     opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
     613             : 
     614             :     /*
     615             :      * If we already searched for this proc and didn't find it, don't bother
     616             :      * searching again.
     617             :      */
     618        8232 :     if (opaque->extra_proc_missing[basenum])
     619        3720 :         return NULL;
     620             : 
     621        4512 :     if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
     622             :     {
     623         200 :         if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
     624             :                                                 procnum)))
     625             :         {
     626         132 :             fmgr_info_copy(&opaque->extra_procinfos[basenum],
     627             :                            index_getprocinfo(bdesc->bd_index, attno, procnum),
     628             :                            bdesc->bd_context);
     629             :         }
     630             :         else
     631             :         {
     632          68 :             opaque->extra_proc_missing[basenum] = true;
     633          68 :             return NULL;
     634             :         }
     635             :     }
     636             : 
     637        4444 :     return &opaque->extra_procinfos[basenum];
     638             : }
     639             : 
     640             : /*
     641             :  * Cache and return the procedure of the given strategy
     642             :  *
     643             :  * Return the procedure corresponding to the given sub-type and strategy
     644             :  * number.  The data type of the index will be used as the left hand side of
     645             :  * the operator and the given sub-type will be used as the right hand side.
     646             :  * Throws an error if the pg_amop row does not exist, but that should not
     647             :  * happen with a properly configured opclass.
     648             :  *
     649             :  * It always throws an error when the data type of the opclass is different
     650             :  * from the data type of the column or the expression.  That happens when the
     651             :  * column data type has implicit cast to the opclass data type.  We don't
     652             :  * bother casting types, because this situation can easily be avoided by
     653             :  * setting storage data type to that of the opclass.  The same problem does not
     654             :  * apply to the data type of the right hand side, because the type in the
     655             :  * ScanKey always matches the opclass' one.
     656             :  *
     657             :  * Note: this function mirrors minmax_get_strategy_procinfo; if changes are
     658             :  * made here, see that function too.
     659             :  */
     660             : static FmgrInfo *
     661       27308 : inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
     662             :                                 uint16 strategynum)
     663             : {
     664             :     InclusionOpaque *opaque;
     665             : 
     666             :     Assert(strategynum >= 1 &&
     667             :            strategynum <= RTMaxStrategyNumber);
     668             : 
     669       27308 :     opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
     670             : 
     671             :     /*
     672             :      * We cache the procedures for the last sub-type in the opaque struct, to
     673             :      * avoid repetitive syscache lookups.  If the sub-type is changed,
     674             :      * invalidate all the cached entries.
     675             :      */
     676       27308 :     if (opaque->cached_subtype != subtype)
     677             :     {
     678             :         uint16      i;
     679             : 
     680        8236 :         for (i = 1; i <= RTMaxStrategyNumber; i++)
     681        7952 :             opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
     682         284 :         opaque->cached_subtype = subtype;
     683             :     }
     684             : 
     685       27308 :     if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
     686             :     {
     687             :         Form_pg_attribute attr;
     688             :         HeapTuple   tuple;
     689             :         Oid         opfamily,
     690             :                     oprid;
     691             :         bool        isNull;
     692             : 
     693         284 :         opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
     694         284 :         attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     695         568 :         tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
     696         284 :                                 ObjectIdGetDatum(attr->atttypid),
     697             :                                 ObjectIdGetDatum(subtype),
     698             :                                 Int16GetDatum(strategynum));
     699             : 
     700         284 :         if (!HeapTupleIsValid(tuple))
     701           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
     702             :                  strategynum, attr->atttypid, subtype, opfamily);
     703             : 
     704         284 :         oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple,
     705             :                                                  Anum_pg_amop_amopopr, &isNull));
     706         284 :         ReleaseSysCache(tuple);
     707             :         Assert(!isNull && RegProcedureIsValid(oprid));
     708             : 
     709         284 :         fmgr_info_cxt(get_opcode(oprid),
     710         284 :                       &opaque->strategy_procinfos[strategynum - 1],
     711             :                       bdesc->bd_context);
     712             :     }
     713             : 
     714       27308 :     return &opaque->strategy_procinfos[strategynum - 1];
     715             : }

Generated by: LCOV version 1.13