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

Generated by: LCOV version 2.0-1