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

Generated by: LCOV version 1.11