LCOV - code coverage report
Current view: top level - src/backend/access/brin - brin_inclusion.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 78.1 % 242 189
Test Date: 2026-01-26 10:56:24 Functions: 83.3 % 6 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 57.0 % 114 65

             Branch data     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                 :        1022 : brin_inclusion_opcinfo(PG_FUNCTION_ARGS)
      95                 :             : {
      96                 :        1022 :         Oid                     typoid = PG_GETARG_OID(0);
      97                 :        1022 :         BrinOpcInfo *result;
      98                 :        1022 :         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                 :        1022 :         result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque));
     110                 :        1022 :         result->oi_nstored = 3;
     111                 :        1022 :         result->oi_regular_nulls = true;
     112                 :        1022 :         result->oi_opaque = (InclusionOpaque *)
     113                 :        1022 :                 MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
     114                 :             : 
     115                 :             :         /* the union */
     116                 :        1022 :         result->oi_typcache[INCLUSION_UNION] =
     117                 :        1022 :                 lookup_type_cache(typoid, 0);
     118                 :             : 
     119                 :             :         /* includes elements that are not mergeable */
     120                 :        1022 :         result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache;
     121                 :             : 
     122                 :             :         /* includes the empty element */
     123                 :        1022 :         result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache;
     124                 :             : 
     125                 :        2044 :         PG_RETURN_POINTER(result);
     126                 :        1022 : }
     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                 :        1371 : brin_inclusion_add_value(PG_FUNCTION_ARGS)
     139                 :             : {
     140                 :        1371 :         BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     141                 :        1371 :         BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     142                 :        1371 :         Datum           newval = PG_GETARG_DATUM(2);
     143                 :        1371 :         bool            isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3);
     144                 :        1371 :         Oid                     colloid = PG_GET_COLLATION();
     145                 :        1371 :         FmgrInfo   *finfo;
     146                 :        1371 :         Datum           result;
     147                 :        1371 :         bool            new = false;
     148                 :        1371 :         AttrNumber      attno;
     149                 :        1371 :         CompactAttribute *attr;
     150                 :             : 
     151         [ +  - ]:        1371 :         Assert(!isnull);
     152                 :             : 
     153                 :        1371 :         attno = column->bv_attno;
     154                 :        1371 :         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         [ +  + ]:        1371 :         if (column->bv_allnulls)
     161                 :             :         {
     162                 :         430 :                 column->bv_values[INCLUSION_UNION] =
     163                 :         430 :                         datumCopy(newval, attr->attbyval, attr->attlen);
     164                 :         430 :                 column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false);
     165                 :         430 :                 column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false);
     166                 :         430 :                 column->bv_allnulls = false;
     167                 :         430 :                 new = true;
     168                 :         430 :         }
     169                 :             : 
     170                 :             :         /*
     171                 :             :          * No need for further processing if the block range is marked as
     172                 :             :          * containing unmergeable values.
     173                 :             :          */
     174         [ +  + ]:        1371 :         if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
     175                 :          70 :                 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                 :        1301 :         finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY, true);
     183   [ +  +  +  + ]:        1301 :         if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval)))
     184                 :             :         {
     185         [ +  + ]:         186 :                 if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]))
     186                 :             :                 {
     187                 :          58 :                         column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
     188                 :          58 :                         PG_RETURN_BOOL(true);
     189                 :             :                 }
     190                 :             : 
     191                 :         128 :                 PG_RETURN_BOOL(false);
     192                 :             :         }
     193                 :             : 
     194         [ +  + ]:        1115 :         if (new)
     195                 :         377 :                 PG_RETURN_BOOL(true);
     196                 :             : 
     197                 :             :         /* Check if the new value is already contained. */
     198                 :         738 :         finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS, true);
     199   [ +  -  +  + ]:         738 :         if (finfo != NULL &&
     200                 :        1476 :                 DatumGetBool(FunctionCall2Coll(finfo, colloid,
     201                 :         738 :                                                                            column->bv_values[INCLUSION_UNION],
     202                 :         738 :                                                                            newval)))
     203                 :         714 :                 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                 :          24 :         finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE, true);
     214   [ +  +  +  + ]:          24 :         if (finfo != NULL &&
     215                 :          44 :                 !DatumGetBool(FunctionCall2Coll(finfo, colloid,
     216                 :          22 :                                                                                 column->bv_values[INCLUSION_UNION],
     217                 :          22 :                                                                                 newval)))
     218                 :             :         {
     219                 :          18 :                 column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
     220                 :          18 :                 PG_RETURN_BOOL(true);
     221                 :             :         }
     222                 :             : 
     223                 :             :         /* Finally, merge the new value to the existing union. */
     224                 :           6 :         finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE, false);
     225                 :          12 :         result = FunctionCall2Coll(finfo, colloid,
     226                 :           6 :                                                            column->bv_values[INCLUSION_UNION], newval);
     227   [ +  -  -  + ]:           6 :         if (!attr->attbyval &&
     228                 :           6 :                 DatumGetPointer(result) != DatumGetPointer(column->bv_values[INCLUSION_UNION]))
     229                 :             :         {
     230                 :           6 :                 pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION]));
     231                 :             : 
     232         [ +  + ]:           6 :                 if (result == newval)
     233                 :           1 :                         result = datumCopy(result, attr->attbyval, attr->attlen);
     234                 :           6 :         }
     235                 :           6 :         column->bv_values[INCLUSION_UNION] = result;
     236                 :             : 
     237                 :           6 :         PG_RETURN_BOOL(true);
     238                 :        1371 : }
     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                 :        7100 : brin_inclusion_consistent(PG_FUNCTION_ARGS)
     251                 :             : {
     252                 :        7100 :         BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     253                 :        7100 :         BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     254                 :        7100 :         ScanKey         key = (ScanKey) PG_GETARG_POINTER(2);
     255                 :        7100 :         Oid                     colloid = PG_GET_COLLATION(),
     256                 :             :                                 subtype;
     257                 :        7100 :         Datum           unionval;
     258                 :        7100 :         AttrNumber      attno;
     259                 :        7100 :         Datum           query;
     260                 :        7100 :         FmgrInfo   *finfo;
     261                 :        7100 :         Datum           result;
     262                 :             : 
     263                 :             :         /* This opclass uses the old signature with only three arguments. */
     264         [ +  - ]:        7100 :         Assert(PG_NARGS() == 3);
     265                 :             : 
     266                 :             :         /* Should not be dealing with all-NULL ranges. */
     267         [ +  - ]:        7100 :         Assert(!column->bv_allnulls);
     268                 :             : 
     269                 :             :         /* It has to be checked, if it contains elements that are not mergeable. */
     270         [ +  + ]:        7100 :         if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
     271                 :         273 :                 PG_RETURN_BOOL(true);
     272                 :             : 
     273                 :        6827 :         attno = key->sk_attno;
     274                 :        6827 :         subtype = key->sk_subtype;
     275                 :        6827 :         query = key->sk_argument;
     276                 :        6827 :         unionval = column->bv_values[INCLUSION_UNION];
     277   [ +  +  +  +  :        6827 :         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                 :             :                 case RTLeftStrategyNumber:
     293                 :         200 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     294                 :             :                                                                                                         RTOverRightStrategyNumber);
     295                 :         200 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     296                 :         200 :                         PG_RETURN_BOOL(!DatumGetBool(result));
     297                 :             : 
     298                 :             :                 case RTOverLeftStrategyNumber:
     299                 :         200 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     300                 :             :                                                                                                         RTRightStrategyNumber);
     301                 :         200 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     302                 :         200 :                         PG_RETURN_BOOL(!DatumGetBool(result));
     303                 :             : 
     304                 :             :                 case RTOverRightStrategyNumber:
     305                 :         200 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     306                 :             :                                                                                                         RTLeftStrategyNumber);
     307                 :         200 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     308                 :         200 :                         PG_RETURN_BOOL(!DatumGetBool(result));
     309                 :             : 
     310                 :             :                 case RTRightStrategyNumber:
     311                 :         200 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     312                 :             :                                                                                                         RTOverLeftStrategyNumber);
     313                 :         200 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     314                 :         200 :                         PG_RETURN_BOOL(!DatumGetBool(result));
     315                 :             : 
     316                 :             :                 case RTBelowStrategyNumber:
     317                 :         100 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     318                 :             :                                                                                                         RTOverAboveStrategyNumber);
     319                 :         100 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     320                 :         100 :                         PG_RETURN_BOOL(!DatumGetBool(result));
     321                 :             : 
     322                 :             :                 case RTOverBelowStrategyNumber:
     323                 :         100 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     324                 :             :                                                                                                         RTAboveStrategyNumber);
     325                 :         100 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     326                 :         100 :                         PG_RETURN_BOOL(!DatumGetBool(result));
     327                 :             : 
     328                 :             :                 case RTOverAboveStrategyNumber:
     329                 :         100 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     330                 :             :                                                                                                         RTBelowStrategyNumber);
     331                 :         100 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     332                 :         100 :                         PG_RETURN_BOOL(!DatumGetBool(result));
     333                 :             : 
     334                 :             :                 case RTAboveStrategyNumber:
     335                 :         100 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     336                 :             :                                                                                                         RTOverBelowStrategyNumber);
     337                 :         100 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     338                 :         100 :                         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                 :             :                 case RTOverlapStrategyNumber:
     349                 :             :                 case RTContainsStrategyNumber:
     350                 :             :                 case RTContainsElemStrategyNumber:
     351                 :             :                 case RTSubStrategyNumber:
     352                 :             :                 case RTSubEqualStrategyNumber:
     353                 :        5120 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     354                 :        2560 :                                                                                                         key->sk_strategy);
     355                 :        2560 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     356                 :        2560 :                         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                 :             :                 case RTContainedByStrategyNumber:
     370                 :             :                 case RTSuperStrategyNumber:
     371                 :             :                 case RTSuperEqualStrategyNumber:
     372                 :        1416 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     373                 :             :                                                                                                         RTOverlapStrategyNumber);
     374                 :        1416 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     375         [ +  + ]:        1416 :                         if (DatumGetBool(result))
     376                 :         844 :                                 PG_RETURN_BOOL(true);
     377                 :             : 
     378                 :         572 :                         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                 :             :                 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                 :             :                 case RTLessStrategyNumber:
     425                 :             :                 case RTLessEqualStrategyNumber:
     426                 :         300 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     427                 :             :                                                                                                         RTRightStrategyNumber);
     428                 :         300 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     429         [ +  + ]:         300 :                         if (!DatumGetBool(result))
     430                 :         250 :                                 PG_RETURN_BOOL(true);
     431                 :             : 
     432                 :          50 :                         PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     433                 :             : 
     434                 :             :                 case RTSameStrategyNumber:
     435                 :             :                 case RTEqualStrategyNumber:
     436                 :         951 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     437                 :             :                                                                                                         RTContainsStrategyNumber);
     438                 :         951 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     439         [ +  + ]:         951 :                         if (DatumGetBool(result))
     440                 :         117 :                                 PG_RETURN_BOOL(true);
     441                 :             : 
     442                 :         834 :                         PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     443                 :             : 
     444                 :             :                 case RTGreaterEqualStrategyNumber:
     445                 :         200 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     446                 :             :                                                                                                         RTLeftStrategyNumber);
     447                 :         200 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     448         [ -  + ]:         200 :                         if (!DatumGetBool(result))
     449                 :         200 :                                 PG_RETURN_BOOL(true);
     450                 :             : 
     451                 :           0 :                         PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
     452                 :             : 
     453                 :             :                 case RTGreaterStrategyNumber:
     454                 :             :                         /* no need to check for empty elements */
     455                 :         200 :                         finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
     456                 :             :                                                                                                         RTLeftStrategyNumber);
     457                 :         200 :                         result = FunctionCall2Coll(finfo, colloid, unionval, query);
     458                 :         200 :                         PG_RETURN_BOOL(!DatumGetBool(result));
     459                 :             : 
     460                 :             :                 default:
     461                 :             :                         /* shouldn't happen */
     462   [ #  #  #  # ]:           0 :                         elog(ERROR, "invalid strategy number %d", key->sk_strategy);
     463                 :           0 :                         PG_RETURN_BOOL(false);
     464                 :             :         }
     465                 :        7100 : }
     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                 :           0 :         AttrNumber      attno;
     481                 :           0 :         CompactAttribute *attr;
     482                 :           0 :         FmgrInfo   *finfo;
     483                 :           0 :         Datum           result;
     484                 :             : 
     485         [ #  # ]:           0 :         Assert(col_a->bv_attno == col_b->bv_attno);
     486         [ #  # ]:           0 :         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                 :           0 :         }
     531                 :           0 :         col_a->bv_values[INCLUSION_UNION] = result;
     532                 :             : 
     533                 :           0 :         PG_RETURN_VOID();
     534                 :           0 : }
     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                 :        2069 : inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum,
     545                 :             :                                            bool missing_ok)
     546                 :             : {
     547                 :        2069 :         InclusionOpaque *opaque;
     548                 :        2069 :         uint16          basenum = procnum - PROCNUM_BASE;
     549                 :             : 
     550                 :             :         /*
     551                 :             :          * We cache these in the opaque struct, to avoid repetitive syscache
     552                 :             :          * lookups.
     553                 :             :          */
     554                 :        2069 :         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         [ +  + ]:        2069 :         if (opaque->extra_proc_missing[basenum])
     561                 :         932 :                 return NULL;
     562                 :             : 
     563         [ +  + ]:        1137 :         if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
     564                 :             :         {
     565         [ +  + ]:          52 :                 if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
     566                 :             :                                                                                                 procnum)))
     567                 :          70 :                         fmgr_info_copy(&opaque->extra_procinfos[basenum],
     568                 :          35 :                                                    index_getprocinfo(bdesc->bd_index, attno, procnum),
     569                 :          35 :                                                    bdesc->bd_context);
     570                 :             :                 else
     571                 :             :                 {
     572         [ +  - ]:          17 :                         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                 :          17 :                         opaque->extra_proc_missing[basenum] = true;
     580                 :          17 :                         return NULL;
     581                 :             :                 }
     582                 :          35 :         }
     583                 :             : 
     584                 :        1120 :         return &opaque->extra_procinfos[basenum];
     585                 :        2069 : }
     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                 :        6827 : inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
     609                 :             :                                                                 uint16 strategynum)
     610                 :             : {
     611                 :        6827 :         InclusionOpaque *opaque;
     612                 :             : 
     613         [ +  - ]:        6827 :         Assert(strategynum >= 1 &&
     614                 :             :                    strategynum <= RTMaxStrategyNumber);
     615                 :             : 
     616                 :        6827 :         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         [ +  + ]:        6827 :         if (opaque->cached_subtype != subtype)
     624                 :             :         {
     625                 :          71 :                 uint16          i;
     626                 :             : 
     627         [ +  + ]:        2201 :                 for (i = 1; i <= RTMaxStrategyNumber; i++)
     628                 :        2130 :                         opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
     629                 :          71 :                 opaque->cached_subtype = subtype;
     630                 :          71 :         }
     631                 :             : 
     632         [ +  + ]:        6827 :         if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
     633                 :             :         {
     634                 :          71 :                 Form_pg_attribute attr;
     635                 :          71 :                 HeapTuple       tuple;
     636                 :          71 :                 Oid                     opfamily,
     637                 :             :                                         oprid;
     638                 :             : 
     639                 :          71 :                 opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
     640                 :          71 :                 attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     641                 :         142 :                 tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
     642                 :          71 :                                                                 ObjectIdGetDatum(attr->atttypid),
     643                 :          71 :                                                                 ObjectIdGetDatum(subtype),
     644                 :          71 :                                                                 UInt16GetDatum(strategynum));
     645                 :             : 
     646         [ +  - ]:          71 :                 if (!HeapTupleIsValid(tuple))
     647   [ #  #  #  # ]:           0 :                         elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
     648                 :             :                                  strategynum, attr->atttypid, subtype, opfamily);
     649                 :             : 
     650                 :          71 :                 oprid = DatumGetObjectId(SysCacheGetAttrNotNull(AMOPSTRATEGY, tuple,
     651                 :             :                                                                                                                 Anum_pg_amop_amopopr));
     652                 :          71 :                 ReleaseSysCache(tuple);
     653         [ +  - ]:          71 :                 Assert(RegProcedureIsValid(oprid));
     654                 :             : 
     655                 :         142 :                 fmgr_info_cxt(get_opcode(oprid),
     656                 :          71 :                                           &opaque->strategy_procinfos[strategynum - 1],
     657                 :          71 :                                           bdesc->bd_context);
     658                 :          71 :         }
     659                 :             : 
     660                 :       13654 :         return &opaque->strategy_procinfos[strategynum - 1];
     661                 :        6827 : }
        

Generated by: LCOV version 2.3.2-1