LCOV - code coverage report
Current view: top level - src/backend/access/brin - brin_inclusion.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 164 210 78.1 %
Date: 2025-04-01 16:15: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-2025, 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        6140 : brin_inclusion_opcinfo(PG_FUNCTION_ARGS)
      95             : {
      96        6140 :     Oid         typoid = PG_GETARG_OID(0);
      97             :     BrinOpcInfo *result;
      98        6140 :     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        6140 :     result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque));
     110        6140 :     result->oi_nstored = 3;
     111        6140 :     result->oi_regular_nulls = true;
     112        6140 :     result->oi_opaque = (InclusionOpaque *)
     113        6140 :         MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
     114             : 
     115             :     /* the union */
     116        6140 :     result->oi_typcache[INCLUSION_UNION] =
     117        6140 :         lookup_type_cache(typoid, 0);
     118             : 
     119             :     /* includes elements that are not mergeable */
     120        6140 :     result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache;
     121             : 
     122             :     /* includes the empty element */
     123        6140 :     result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache;
     124             : 
     125        6140 :     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        8226 : brin_inclusion_add_value(PG_FUNCTION_ARGS)
     139             : {
     140        8226 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     141        8226 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     142        8226 :     Datum       newval = PG_GETARG_DATUM(2);
     143        8226 :     bool        isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3);
     144        8226 :     Oid         colloid = PG_GET_COLLATION();
     145             :     FmgrInfo   *finfo;
     146             :     Datum       result;
     147        8226 :     bool        new = false;
     148             :     AttrNumber  attno;
     149             :     CompactAttribute *attr;
     150             : 
     151             :     Assert(!isnull);
     152             : 
     153        8226 :     attno = column->bv_attno;
     154        8226 :     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        8226 :     if (column->bv_allnulls)
     161             :     {
     162        5160 :         column->bv_values[INCLUSION_UNION] =
     163        2580 :             datumCopy(newval, attr->attbyval, attr->attlen);
     164        2580 :         column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false);
     165        2580 :         column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false);
     166        2580 :         column->bv_allnulls = false;
     167        2580 :         new = true;
     168             :     }
     169             : 
     170             :     /*
     171             :      * No need for further processing if the block range is marked as
     172             :      * containing unmergeable values.
     173             :      */
     174        8226 :     if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
     175         420 :         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        7806 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY, true);
     183        7806 :     if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval)))
     184             :     {
     185        1116 :         if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]))
     186             :         {
     187         348 :             column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
     188         348 :             PG_RETURN_BOOL(true);
     189             :         }
     190             : 
     191         768 :         PG_RETURN_BOOL(false);
     192             :     }
     193             : 
     194        6690 :     if (new)
     195        2262 :         PG_RETURN_BOOL(true);
     196             : 
     197             :     /* Check if the new value is already contained. */
     198        4428 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS, true);
     199        8856 :     if (finfo != NULL &&
     200        4428 :         DatumGetBool(FunctionCall2Coll(finfo, colloid,
     201        4428 :                                        column->bv_values[INCLUSION_UNION],
     202             :                                        newval)))
     203        4284 :         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         144 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE, true);
     214         144 :     if (finfo != NULL &&
     215         132 :         !DatumGetBool(FunctionCall2Coll(finfo, colloid,
     216         132 :                                         column->bv_values[INCLUSION_UNION],
     217             :                                         newval)))
     218             :     {
     219         108 :         column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
     220         108 :         PG_RETURN_BOOL(true);
     221             :     }
     222             : 
     223             :     /* Finally, merge the new value to the existing union. */
     224          36 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE, false);
     225          36 :     result = FunctionCall2Coll(finfo, colloid,
     226          36 :                                column->bv_values[INCLUSION_UNION], newval);
     227          72 :     if (!attr->attbyval &&
     228          36 :         DatumGetPointer(result) != DatumGetPointer(column->bv_values[INCLUSION_UNION]))
     229             :     {
     230          36 :         pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION]));
     231             : 
     232          36 :         if (result == newval)
     233           6 :             result = datumCopy(result, attr->attbyval, attr->attlen);
     234             :     }
     235          36 :     column->bv_values[INCLUSION_UNION] = result;
     236             : 
     237          36 :     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       42600 : brin_inclusion_consistent(PG_FUNCTION_ARGS)
     251             : {
     252       42600 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     253       42600 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     254       42600 :     ScanKey     key = (ScanKey) PG_GETARG_POINTER(2);
     255       42600 :     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       42600 :     if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
     271        1638 :         PG_RETURN_BOOL(true);
     272             : 
     273       40962 :     attno = key->sk_attno;
     274       40962 :     subtype = key->sk_subtype;
     275       40962 :     query = key->sk_argument;
     276       40962 :     unionval = column->bv_values[INCLUSION_UNION];
     277       40962 :     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        1200 :         case RTLeftStrategyNumber:
     293        1200 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     294             :                                                     RTOverRightStrategyNumber);
     295        1200 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     296        1200 :             PG_RETURN_BOOL(!DatumGetBool(result));
     297             : 
     298        1200 :         case RTOverLeftStrategyNumber:
     299        1200 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     300             :                                                     RTRightStrategyNumber);
     301        1200 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     302        1200 :             PG_RETURN_BOOL(!DatumGetBool(result));
     303             : 
     304        1200 :         case RTOverRightStrategyNumber:
     305        1200 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     306             :                                                     RTLeftStrategyNumber);
     307        1200 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     308        1200 :             PG_RETURN_BOOL(!DatumGetBool(result));
     309             : 
     310        1200 :         case RTRightStrategyNumber:
     311        1200 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     312             :                                                     RTOverLeftStrategyNumber);
     313        1200 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     314        1200 :             PG_RETURN_BOOL(!DatumGetBool(result));
     315             : 
     316         600 :         case RTBelowStrategyNumber:
     317         600 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     318             :                                                     RTOverAboveStrategyNumber);
     319         600 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     320         600 :             PG_RETURN_BOOL(!DatumGetBool(result));
     321             : 
     322         600 :         case RTOverBelowStrategyNumber:
     323         600 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     324             :                                                     RTAboveStrategyNumber);
     325         600 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     326         600 :             PG_RETURN_BOOL(!DatumGetBool(result));
     327             : 
     328         600 :         case RTOverAboveStrategyNumber:
     329         600 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     330             :                                                     RTBelowStrategyNumber);
     331         600 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     332         600 :             PG_RETURN_BOOL(!DatumGetBool(result));
     333             : 
     334         600 :         case RTAboveStrategyNumber:
     335         600 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     336             :                                                     RTOverBelowStrategyNumber);
     337         600 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     338         600 :             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       15360 :         case RTOverlapStrategyNumber:
     349             :         case RTContainsStrategyNumber:
     350             :         case RTContainsElemStrategyNumber:
     351             :         case RTSubStrategyNumber:
     352             :         case RTSubEqualStrategyNumber:
     353       15360 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     354       15360 :                                                     key->sk_strategy);
     355       15360 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     356       15360 :             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        8496 :         case RTContainedByStrategyNumber:
     370             :         case RTSuperStrategyNumber:
     371             :         case RTSuperEqualStrategyNumber:
     372        8496 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     373             :                                                     RTOverlapStrategyNumber);
     374        8496 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     375        8496 :             if (DatumGetBool(result))
     376        5064 :                 PG_RETURN_BOOL(true);
     377             : 
     378        3432 :             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        1800 :         case RTLessStrategyNumber:
     425             :         case RTLessEqualStrategyNumber:
     426        1800 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     427             :                                                     RTRightStrategyNumber);
     428        1800 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     429        1800 :             if (!DatumGetBool(result))
     430        1500 :                 PG_RETURN_BOOL(true);
     431             : 
     432         300 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     433             : 
     434        5706 :         case RTSameStrategyNumber:
     435             :         case RTEqualStrategyNumber:
     436        5706 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     437             :                                                     RTContainsStrategyNumber);
     438        5706 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     439        5706 :             if (DatumGetBool(result))
     440         702 :                 PG_RETURN_BOOL(true);
     441             : 
     442        5004 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     443             : 
     444        1200 :         case RTGreaterEqualStrategyNumber:
     445        1200 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     446             :                                                     RTLeftStrategyNumber);
     447        1200 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     448        1200 :             if (!DatumGetBool(result))
     449        1200 :                 PG_RETURN_BOOL(true);
     450             : 
     451           0 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     452             : 
     453        1200 :         case RTGreaterStrategyNumber:
     454             :             /* no need to check for empty elements */
     455        1200 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     456             :                                                     RTLeftStrategyNumber);
     457        1200 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     458        1200 :             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       12414 : inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum,
     545             :                        bool missing_ok)
     546             : {
     547             :     InclusionOpaque *opaque;
     548       12414 :     uint16      basenum = procnum - PROCNUM_BASE;
     549             : 
     550             :     /*
     551             :      * We cache these in the opaque struct, to avoid repetitive syscache
     552             :      * lookups.
     553             :      */
     554       12414 :     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       12414 :     if (opaque->extra_proc_missing[basenum])
     561        5592 :         return NULL;
     562             : 
     563        6822 :     if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
     564             :     {
     565         312 :         if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
     566             :                                                 procnum)))
     567         210 :             fmgr_info_copy(&opaque->extra_procinfos[basenum],
     568             :                            index_getprocinfo(bdesc->bd_index, attno, procnum),
     569             :                            bdesc->bd_context);
     570             :         else
     571             :         {
     572         102 :             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         102 :             opaque->extra_proc_missing[basenum] = true;
     580         102 :             return NULL;
     581             :         }
     582             :     }
     583             : 
     584        6720 :     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       40962 : 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       40962 :     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       40962 :     if (opaque->cached_subtype != subtype)
     624             :     {
     625             :         uint16      i;
     626             : 
     627       13206 :         for (i = 1; i <= RTMaxStrategyNumber; i++)
     628       12780 :             opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
     629         426 :         opaque->cached_subtype = subtype;
     630             :     }
     631             : 
     632       40962 :     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         426 :         opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
     640         426 :         attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     641         426 :         tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
     642             :                                 ObjectIdGetDatum(attr->atttypid),
     643             :                                 ObjectIdGetDatum(subtype),
     644             :                                 Int16GetDatum(strategynum));
     645             : 
     646         426 :         if (!HeapTupleIsValid(tuple))
     647           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
     648             :                  strategynum, attr->atttypid, subtype, opfamily);
     649             : 
     650         426 :         oprid = DatumGetObjectId(SysCacheGetAttrNotNull(AMOPSTRATEGY, tuple,
     651             :                                                         Anum_pg_amop_amopopr));
     652         426 :         ReleaseSysCache(tuple);
     653             :         Assert(RegProcedureIsValid(oprid));
     654             : 
     655         426 :         fmgr_info_cxt(get_opcode(oprid),
     656         426 :                       &opaque->strategy_procinfos[strategynum - 1],
     657             :                       bdesc->bd_context);
     658             :     }
     659             : 
     660       40962 :     return &opaque->strategy_procinfos[strategynum - 1];
     661             : }

Generated by: LCOV version 1.14