LCOV - differential code coverage report
Current view: top level - src/backend/access/brin - brin_inclusion.c (source / functions) Coverage Total Hit UIC UBC GBC GIC GNC CBC ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 77.9 % 217 169 1 47 1 3 2 163 5 1
Current Date: 2023-04-08 15:15:32 Functions: 83.3 % 6 5 1 1 4
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  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-2023, PostgreSQL Global Development Group
      20                 :  * Portions Copyright (c) 1994, Regents of the University of California
      21                 :  *
      22                 :  * IDENTIFICATION
      23                 :  *    src/backend/access/brin/brin_inclusion.c
      24                 :  */
      25                 : #include "postgres.h"
      26                 : 
      27                 : #include "access/brin_internal.h"
      28                 : #include "access/brin_tuple.h"
      29                 : #include "access/genam.h"
      30                 : #include "access/skey.h"
      31                 : #include "catalog/pg_amop.h"
      32                 : #include "catalog/pg_type.h"
      33                 : #include "utils/builtins.h"
      34                 : #include "utils/datum.h"
      35                 : #include "utils/lsyscache.h"
      36                 : #include "utils/rel.h"
      37                 : #include "utils/syscache.h"
      38                 : 
      39                 : 
      40                 : /*
      41                 :  * Additional SQL level support functions
      42                 :  *
      43                 :  * Procedure numbers must not use values reserved for BRIN itself; see
      44                 :  * brin_internal.h.
      45                 :  */
      46                 : #define     INCLUSION_MAX_PROCNUMS  4   /* maximum support procs we need */
      47                 : #define     PROCNUM_MERGE           11  /* required */
      48                 : #define     PROCNUM_MERGEABLE       12  /* optional */
      49                 : #define     PROCNUM_CONTAINS        13  /* optional */
      50                 : #define     PROCNUM_EMPTY           14  /* optional */
      51                 : 
      52                 : 
      53                 : /*
      54                 :  * Subtract this from procnum to obtain index in InclusionOpaque arrays
      55                 :  * (Must be equal to minimum of private procnums).
      56                 :  */
      57                 : #define     PROCNUM_BASE            11
      58                 : 
      59                 : /*-
      60                 :  * The values stored in the bv_values arrays correspond to:
      61                 :  *
      62                 :  * INCLUSION_UNION
      63                 :  *      the union of the values in the block range
      64                 :  * INCLUSION_UNMERGEABLE
      65                 :  *      whether the values in the block range cannot be merged
      66                 :  *      (e.g. an IPv6 address amidst IPv4 addresses)
      67                 :  * INCLUSION_CONTAINS_EMPTY
      68                 :  *      whether an empty value is present in any tuple
      69                 :  *      in the block range
      70                 :  */
      71                 : #define INCLUSION_UNION             0
      72                 : #define INCLUSION_UNMERGEABLE       1
      73                 : #define INCLUSION_CONTAINS_EMPTY    2
      74                 : 
      75                 : 
      76                 : typedef struct InclusionOpaque
      77                 : {
      78                 :     FmgrInfo    extra_procinfos[INCLUSION_MAX_PROCNUMS];
      79                 :     bool        extra_proc_missing[INCLUSION_MAX_PROCNUMS];
      80                 :     Oid         cached_subtype;
      81                 :     FmgrInfo    strategy_procinfos[RTMaxStrategyNumber];
      82                 : } InclusionOpaque;
      83                 : 
      84                 : static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno,
      85                 :                                         uint16 procnum);
      86                 : static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno,
      87                 :                                                  Oid subtype, uint16 strategynum);
      88                 : 
      89                 : 
      90                 : /*
      91                 :  * BRIN inclusion OpcInfo function
      92                 :  */
      93                 : Datum
      94 CBC        3058 : brin_inclusion_opcinfo(PG_FUNCTION_ARGS)
      95                 : {
      96            3058 :     Oid         typoid = PG_GETARG_OID(0);
      97                 :     BrinOpcInfo *result;
      98            3058 :     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            3058 :     result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque));
     110            3058 :     result->oi_nstored = 3;
     111            3058 :     result->oi_regular_nulls = true;
     112            3058 :     result->oi_opaque = (InclusionOpaque *)
     113            3058 :         MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
     114                 : 
     115                 :     /* the union */
     116            3058 :     result->oi_typcache[INCLUSION_UNION] =
     117            3058 :         lookup_type_cache(typoid, 0);
     118                 : 
     119                 :     /* includes elements that are not mergeable */
     120            3058 :     result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache;
     121                 : 
     122                 :     /* includes the empty element */
     123            3058 :     result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache;
     124                 : 
     125            3058 :     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                 :     Form_pg_attribute attr;
     150                 : 
     151            4113 :     Assert(!isnull);
     152                 : 
     153            4113 :     attno = column->bv_attno;
     154            4113 :     attr = TupleDescAttr(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);
     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);
     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);
     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);
     225              18 :     Assert(finfo != NULL);
     226              18 :     result = FunctionCall2Coll(finfo, colloid,
     227              18 :                                column->bv_values[INCLUSION_UNION], newval);
     228              36 :     if (!attr->attbyval &&
     229              18 :         DatumGetPointer(result) != DatumGetPointer(column->bv_values[INCLUSION_UNION]))
     230                 :     {
     231              18 :         pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION]));
     232                 : 
     233              18 :         if (result == newval)
     234               3 :             result = datumCopy(result, attr->attbyval, attr->attlen);
     235                 :     }
     236              18 :     column->bv_values[INCLUSION_UNION] = result;
     237                 : 
     238              18 :     PG_RETURN_BOOL(true);
     239                 : }
     240                 : 
     241                 : /*
     242                 :  * BRIN inclusion consistent function
     243                 :  *
     244                 :  * We're no longer dealing with NULL keys in the consistent function, that is
     245                 :  * now handled by the AM code. That means we should not get any all-NULL ranges
     246                 :  * either, because those can't be consistent with regular (not [IS] NULL) keys.
     247                 :  *
     248                 :  * All of the strategies are optional.
     249                 :  */
     250                 : Datum
     251           21300 : brin_inclusion_consistent(PG_FUNCTION_ARGS)
     252                 : {
     253           21300 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     254           21300 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     255           21300 :     ScanKey     key = (ScanKey) PG_GETARG_POINTER(2);
     256           21300 :     Oid         colloid = PG_GET_COLLATION(),
     257                 :                 subtype;
     258                 :     Datum       unionval;
     259                 :     AttrNumber  attno;
     260                 :     Datum       query;
     261                 :     FmgrInfo   *finfo;
     262                 :     Datum       result;
     263                 : 
     264                 :     /* This opclass uses the old signature with only three arguments. */
     265           21300 :     Assert(PG_NARGS() == 3);
     266                 : 
     267                 :     /* Should not be dealing with all-NULL ranges. */
     268           21300 :     Assert(!column->bv_allnulls);
     269                 : 
     270                 :     /* It has to be checked, if it contains elements that are not mergeable. */
     271           21300 :     if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
     272             819 :         PG_RETURN_BOOL(true);
     273                 : 
     274           20481 :     attno = key->sk_attno;
     275           20481 :     subtype = key->sk_subtype;
     276           20481 :     query = key->sk_argument;
     277           20481 :     unionval = column->bv_values[INCLUSION_UNION];
     278           20481 :     switch (key->sk_strategy)
     279                 :     {
     280                 :             /*
     281                 :              * Placement strategies
     282                 :              *
     283                 :              * These are implemented by logically negating the result of the
     284                 :              * converse placement operator; for this to work, the converse
     285                 :              * operator must be part of the opclass.  An error will be thrown
     286                 :              * by inclusion_get_strategy_procinfo() if the required strategy
     287                 :              * is not part of the opclass.
     288                 :              *
     289                 :              * These all return false if either argument is empty, so there is
     290                 :              * no need to check for empty elements.
     291                 :              */
     292                 : 
     293             600 :         case RTLeftStrategyNumber:
     294             600 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     295                 :                                                     RTOverRightStrategyNumber);
     296             600 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     297             600 :             PG_RETURN_BOOL(!DatumGetBool(result));
     298                 : 
     299             600 :         case RTOverLeftStrategyNumber:
     300             600 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     301                 :                                                     RTRightStrategyNumber);
     302             600 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     303             600 :             PG_RETURN_BOOL(!DatumGetBool(result));
     304                 : 
     305             600 :         case RTOverRightStrategyNumber:
     306             600 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     307                 :                                                     RTLeftStrategyNumber);
     308             600 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     309             600 :             PG_RETURN_BOOL(!DatumGetBool(result));
     310                 : 
     311             600 :         case RTRightStrategyNumber:
     312             600 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     313                 :                                                     RTOverLeftStrategyNumber);
     314             600 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     315             600 :             PG_RETURN_BOOL(!DatumGetBool(result));
     316                 : 
     317             300 :         case RTBelowStrategyNumber:
     318             300 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     319                 :                                                     RTOverAboveStrategyNumber);
     320             300 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     321             300 :             PG_RETURN_BOOL(!DatumGetBool(result));
     322                 : 
     323             300 :         case RTOverBelowStrategyNumber:
     324             300 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     325                 :                                                     RTAboveStrategyNumber);
     326             300 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     327             300 :             PG_RETURN_BOOL(!DatumGetBool(result));
     328                 : 
     329             300 :         case RTOverAboveStrategyNumber:
     330             300 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     331                 :                                                     RTBelowStrategyNumber);
     332             300 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     333             300 :             PG_RETURN_BOOL(!DatumGetBool(result));
     334                 : 
     335             300 :         case RTAboveStrategyNumber:
     336             300 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     337                 :                                                     RTOverBelowStrategyNumber);
     338             300 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     339             300 :             PG_RETURN_BOOL(!DatumGetBool(result));
     340                 : 
     341                 :             /*
     342                 :              * Overlap and contains strategies
     343                 :              *
     344                 :              * These strategies are simple enough that we can simply call the
     345                 :              * operator and return its result.  Empty elements don't change
     346                 :              * the result.
     347                 :              */
     348                 : 
     349            7680 :         case RTOverlapStrategyNumber:
     350                 :         case RTContainsStrategyNumber:
     351                 :         case RTContainsElemStrategyNumber:
     352                 :         case RTSubStrategyNumber:
     353                 :         case RTSubEqualStrategyNumber:
     354            7680 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     355            7680 :                                                     key->sk_strategy);
     356            7680 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     357            7680 :             PG_RETURN_DATUM(result);
     358                 : 
     359                 :             /*
     360                 :              * Contained by strategies
     361                 :              *
     362                 :              * We cannot just call the original operator for the contained by
     363                 :              * strategies because some elements can be contained even though
     364                 :              * the union is not; instead we use the overlap operator.
     365                 :              *
     366                 :              * We check for empty elements separately as they are not merged
     367                 :              * to the union but contained by everything.
     368                 :              */
     369                 : 
     370            4248 :         case RTContainedByStrategyNumber:
     371                 :         case RTSuperStrategyNumber:
     372                 :         case RTSuperEqualStrategyNumber:
     373            4248 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     374                 :                                                     RTOverlapStrategyNumber);
     375            4248 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     376            4248 :             if (DatumGetBool(result))
     377            2532 :                 PG_RETURN_BOOL(true);
     378                 : 
     379            1716 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     380                 : 
     381                 :             /*
     382                 :              * Adjacent strategy
     383                 :              *
     384                 :              * We test for overlap first but to be safe we need to call the
     385                 :              * actual adjacent operator also.
     386                 :              *
     387                 :              * An empty element cannot be adjacent to any other, so there is
     388                 :              * no need to check for it.
     389                 :              */
     390                 : 
     391 UBC           0 :         case RTAdjacentStrategyNumber:
     392               0 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     393                 :                                                     RTOverlapStrategyNumber);
     394               0 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     395               0 :             if (DatumGetBool(result))
     396               0 :                 PG_RETURN_BOOL(true);
     397                 : 
     398               0 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     399                 :                                                     RTAdjacentStrategyNumber);
     400               0 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     401               0 :             PG_RETURN_DATUM(result);
     402                 : 
     403                 :             /*
     404                 :              * Basic comparison strategies
     405                 :              *
     406                 :              * It is straightforward to support the equality strategies with
     407                 :              * the contains operator.  Generally, inequality strategies do not
     408                 :              * make much sense for the types which will be used with the
     409                 :              * inclusion BRIN family of opclasses, but it is possible to
     410                 :              * implement them with logical negation of the left-of and
     411                 :              * right-of operators.
     412                 :              *
     413                 :              * NB: These strategies cannot be used with geometric datatypes
     414                 :              * that use comparison of areas!  The only exception is the "same"
     415                 :              * strategy.
     416                 :              *
     417                 :              * Empty elements are considered to be less than the others.  We
     418                 :              * cannot use the empty support function to check the query is an
     419                 :              * empty element, because the query can be another data type than
     420                 :              * the empty support function argument.  So we will return true,
     421                 :              * if there is a possibility that empty elements will change the
     422                 :              * result.
     423                 :              */
     424                 : 
     425 CBC         900 :         case RTLessStrategyNumber:
     426                 :         case RTLessEqualStrategyNumber:
     427             900 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     428                 :                                                     RTRightStrategyNumber);
     429             900 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     430             900 :             if (!DatumGetBool(result))
     431             750 :                 PG_RETURN_BOOL(true);
     432                 : 
     433             150 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     434                 : 
     435            2853 :         case RTSameStrategyNumber:
     436                 :         case RTEqualStrategyNumber:
     437            2853 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     438                 :                                                     RTContainsStrategyNumber);
     439            2853 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     440            2853 :             if (DatumGetBool(result))
     441             351 :                 PG_RETURN_BOOL(true);
     442                 : 
     443            2502 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     444                 : 
     445             600 :         case RTGreaterEqualStrategyNumber:
     446             600 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     447                 :                                                     RTLeftStrategyNumber);
     448             600 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     449             600 :             if (!DatumGetBool(result))
     450             600 :                 PG_RETURN_BOOL(true);
     451                 : 
     452 UBC           0 :             PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     453                 : 
     454 CBC         600 :         case RTGreaterStrategyNumber:
     455                 :             /* no need to check for empty elements */
     456             600 :             finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     457                 :                                                     RTLeftStrategyNumber);
     458             600 :             result = FunctionCall2Coll(finfo, colloid, unionval, query);
     459             600 :             PG_RETURN_BOOL(!DatumGetBool(result));
     460                 : 
     461 UBC           0 :         default:
     462                 :             /* shouldn't happen */
     463               0 :             elog(ERROR, "invalid strategy number %d", key->sk_strategy);
     464                 :             PG_RETURN_BOOL(false);
     465                 :     }
     466                 : }
     467                 : 
     468                 : /*
     469                 :  * BRIN inclusion union function
     470                 :  *
     471                 :  * Given two BrinValues, update the first of them as a union of the summary
     472                 :  * values contained in both.  The second one is untouched.
     473                 :  */
     474                 : Datum
     475               0 : brin_inclusion_union(PG_FUNCTION_ARGS)
     476                 : {
     477               0 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     478               0 :     BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
     479               0 :     BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
     480               0 :     Oid         colloid = PG_GET_COLLATION();
     481                 :     AttrNumber  attno;
     482                 :     Form_pg_attribute attr;
     483                 :     FmgrInfo   *finfo;
     484                 :     Datum       result;
     485                 : 
     486               0 :     Assert(col_a->bv_attno == col_b->bv_attno);
     487               0 :     Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
     488                 : 
     489               0 :     attno = col_a->bv_attno;
     490               0 :     attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     491                 : 
     492                 :     /* If B includes empty elements, mark A similarly, if needed. */
     493               0 :     if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&
     494               0 :         DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY]))
     495               0 :         col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
     496                 : 
     497                 :     /* Check if A includes elements that are not mergeable. */
     498               0 :     if (DatumGetBool(col_a->bv_values[INCLUSION_UNMERGEABLE]))
     499               0 :         PG_RETURN_VOID();
     500                 : 
     501                 :     /* If B includes elements that are not mergeable, mark A similarly. */
     502               0 :     if (DatumGetBool(col_b->bv_values[INCLUSION_UNMERGEABLE]))
     503                 :     {
     504               0 :         col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
     505               0 :         PG_RETURN_VOID();
     506                 :     }
     507                 : 
     508                 :     /* Check if A and B are mergeable; if not, mark A unmergeable. */
     509               0 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
     510               0 :     if (finfo != NULL &&
     511               0 :         !DatumGetBool(FunctionCall2Coll(finfo, colloid,
     512               0 :                                         col_a->bv_values[INCLUSION_UNION],
     513               0 :                                         col_b->bv_values[INCLUSION_UNION])))
     514                 :     {
     515               0 :         col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
     516               0 :         PG_RETURN_VOID();
     517                 :     }
     518                 : 
     519                 :     /* Finally, merge B to A. */
     520               0 :     finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
     521               0 :     Assert(finfo != NULL);
     522               0 :     result = FunctionCall2Coll(finfo, colloid,
     523               0 :                                col_a->bv_values[INCLUSION_UNION],
     524               0 :                                col_b->bv_values[INCLUSION_UNION]);
     525               0 :     if (!attr->attbyval &&
     526               0 :         DatumGetPointer(result) != DatumGetPointer(col_a->bv_values[INCLUSION_UNION]))
     527                 :     {
     528               0 :         pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION]));
     529                 : 
     530               0 :         if (result == col_b->bv_values[INCLUSION_UNION])
     531               0 :             result = datumCopy(result, attr->attbyval, attr->attlen);
     532                 :     }
     533               0 :     col_a->bv_values[INCLUSION_UNION] = result;
     534                 : 
     535               0 :     PG_RETURN_VOID();
     536                 : }
     537                 : 
     538                 : /*
     539                 :  * Cache and return inclusion opclass support procedure
     540                 :  *
     541                 :  * Return the procedure corresponding to the given function support number
     542                 :  * or null if it is not exists.
     543                 :  */
     544                 : static FmgrInfo *
     545 CBC        6207 : inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
     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                 :         {
     568             105 :             fmgr_info_copy(&opaque->extra_procinfos[basenum],
     569                 :                            index_getprocinfo(bdesc->bd_index, attno, procnum),
     570                 :                            bdesc->bd_context);
     571                 :         }
     572                 :         else
     573                 :         {
     574              51 :             opaque->extra_proc_missing[basenum] = true;
     575              51 :             return NULL;
     576                 :         }
     577                 :     }
     578                 : 
     579            3360 :     return &opaque->extra_procinfos[basenum];
     580                 : }
     581                 : 
     582                 : /*
     583                 :  * Cache and return the procedure of the given strategy
     584                 :  *
     585                 :  * Return the procedure corresponding to the given sub-type and strategy
     586                 :  * number.  The data type of the index will be used as the left hand side of
     587                 :  * the operator and the given sub-type will be used as the right hand side.
     588                 :  * Throws an error if the pg_amop row does not exist, but that should not
     589                 :  * happen with a properly configured opclass.
     590                 :  *
     591                 :  * It always throws an error when the data type of the opclass is different
     592                 :  * from the data type of the column or the expression.  That happens when the
     593                 :  * column data type has implicit cast to the opclass data type.  We don't
     594                 :  * bother casting types, because this situation can easily be avoided by
     595                 :  * setting storage data type to that of the opclass.  The same problem does not
     596                 :  * apply to the data type of the right hand side, because the type in the
     597                 :  * ScanKey always matches the opclass' one.
     598                 :  *
     599                 :  * Note: this function mirrors minmax_get_strategy_procinfo; if changes are
     600                 :  * made here, see that function too.
     601                 :  */
     602                 : static FmgrInfo *
     603           20481 : inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
     604                 :                                 uint16 strategynum)
     605                 : {
     606                 :     InclusionOpaque *opaque;
     607                 : 
     608           20481 :     Assert(strategynum >= 1 &&
     609                 :            strategynum <= RTMaxStrategyNumber);
     610                 : 
     611           20481 :     opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
     612                 : 
     613                 :     /*
     614                 :      * We cache the procedures for the last sub-type in the opaque struct, to
     615                 :      * avoid repetitive syscache lookups.  If the sub-type is changed,
     616                 :      * invalidate all the cached entries.
     617                 :      */
     618           20481 :     if (opaque->cached_subtype != subtype)
     619                 :     {
     620                 :         uint16      i;
     621                 : 
     622            6603 :         for (i = 1; i <= RTMaxStrategyNumber; i++)
     623            6390 :             opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
     624             213 :         opaque->cached_subtype = subtype;
     625                 :     }
     626                 : 
     627           20481 :     if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
     628                 :     {
     629                 :         Form_pg_attribute attr;
     630                 :         HeapTuple   tuple;
     631                 :         Oid         opfamily,
     632                 :                     oprid;
     633 ECB             : 
     634 CBC         213 :         opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
     635             213 :         attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     636 GIC         213 :         tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
     637                 :                                 ObjectIdGetDatum(attr->atttypid),
     638                 :                                 ObjectIdGetDatum(subtype),
     639                 :                                 Int16GetDatum(strategynum));
     640 ECB             : 
     641 GBC         213 :         if (!HeapTupleIsValid(tuple))
     642 UIC           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
     643                 :                  strategynum, attr->atttypid, subtype, opfamily);
     644 ECB             : 
     645 GNC         213 :         oprid = DatumGetObjectId(SysCacheGetAttrNotNull(AMOPSTRATEGY, tuple,
     646                 :                                                         Anum_pg_amop_amopopr));
     647 CBC         213 :         ReleaseSysCache(tuple);
     648 GNC         213 :         Assert(RegProcedureIsValid(oprid));
     649 ECB             : 
     650 CBC         213 :         fmgr_info_cxt(get_opcode(oprid),
     651 GIC         213 :                       &opaque->strategy_procinfos[strategynum - 1],
     652                 :                       bdesc->bd_context);
     653                 :     }
     654 ECB             : 
     655 GIC       20481 :     return &opaque->strategy_procinfos[strategynum - 1];
     656                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a