LCOV - code coverage report
Current view: top level - src/backend/access/brin - brin_minmax.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 75.7 % 152 115
Test Date: 2026-01-26 10:56:24 Functions: 80.0 % 5 4
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 50.0 % 54 27

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * brin_minmax.c
       3                 :             :  *              Implementation of Min/Max opclass for BRIN
       4                 :             :  *
       5                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       6                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       7                 :             :  *
       8                 :             :  * IDENTIFICATION
       9                 :             :  *        src/backend/access/brin/brin_minmax.c
      10                 :             :  */
      11                 :             : #include "postgres.h"
      12                 :             : 
      13                 :             : #include "access/brin_internal.h"
      14                 :             : #include "access/brin_tuple.h"
      15                 :             : #include "access/stratnum.h"
      16                 :             : #include "catalog/pg_amop.h"
      17                 :             : #include "utils/datum.h"
      18                 :             : #include "utils/fmgrprotos.h"
      19                 :             : #include "utils/lsyscache.h"
      20                 :             : #include "utils/rel.h"
      21                 :             : #include "utils/syscache.h"
      22                 :             : 
      23                 :             : typedef struct MinmaxOpaque
      24                 :             : {
      25                 :             :         Oid                     cached_subtype;
      26                 :             :         FmgrInfo        strategy_procinfos[BTMaxStrategyNumber];
      27                 :             : } MinmaxOpaque;
      28                 :             : 
      29                 :             : static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno,
      30                 :             :                                                                                           Oid subtype, uint16 strategynum);
      31                 :             : 
      32                 :             : 
      33                 :             : Datum
      34                 :        6777 : brin_minmax_opcinfo(PG_FUNCTION_ARGS)
      35                 :             : {
      36                 :        6777 :         Oid                     typoid = PG_GETARG_OID(0);
      37                 :        6777 :         BrinOpcInfo *result;
      38                 :             : 
      39                 :             :         /*
      40                 :             :          * opaque->strategy_procinfos is initialized lazily; here it is set to
      41                 :             :          * all-uninitialized by palloc0 which sets fn_oid to InvalidOid.
      42                 :             :          */
      43                 :             : 
      44                 :        6777 :         result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
      45                 :             :                                          sizeof(MinmaxOpaque));
      46                 :        6777 :         result->oi_nstored = 2;
      47                 :        6777 :         result->oi_regular_nulls = true;
      48                 :        6777 :         result->oi_opaque = (MinmaxOpaque *)
      49                 :        6777 :                 MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
      50                 :        6777 :         result->oi_typcache[0] = result->oi_typcache[1] =
      51                 :        6777 :                 lookup_type_cache(typoid, 0);
      52                 :             : 
      53                 :       13554 :         PG_RETURN_POINTER(result);
      54                 :        6777 : }
      55                 :             : 
      56                 :             : /*
      57                 :             :  * Examine the given index tuple (which contains partial status of a certain
      58                 :             :  * page range) by comparing it to the given value that comes from another heap
      59                 :             :  * tuple.  If the new value is outside the min/max range specified by the
      60                 :             :  * existing tuple values, update the index tuple and return true.  Otherwise,
      61                 :             :  * return false and do not modify in this case.
      62                 :             :  */
      63                 :             : Datum
      64                 :      171664 : brin_minmax_add_value(PG_FUNCTION_ARGS)
      65                 :             : {
      66                 :      171664 :         BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
      67                 :      171664 :         BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
      68                 :      171664 :         Datum           newval = PG_GETARG_DATUM(2);
      69                 :      171664 :         bool            isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3);
      70                 :      171664 :         Oid                     colloid = PG_GET_COLLATION();
      71                 :      171664 :         FmgrInfo   *cmpFn;
      72                 :      171664 :         Datum           compar;
      73                 :      171664 :         bool            updated = false;
      74                 :      171664 :         Form_pg_attribute attr;
      75                 :      171664 :         AttrNumber      attno;
      76                 :             : 
      77         [ +  - ]:      171664 :         Assert(!isnull);
      78                 :             : 
      79                 :      171664 :         attno = column->bv_attno;
      80                 :      171664 :         attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
      81                 :             : 
      82                 :             :         /*
      83                 :             :          * If the recorded value is null, store the new value (which we know to be
      84                 :             :          * not null) as both minimum and maximum, and we're done.
      85                 :             :          */
      86         [ +  + ]:      171664 :         if (column->bv_allnulls)
      87                 :             :         {
      88                 :        2960 :                 column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
      89                 :        2960 :                 column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
      90                 :        2960 :                 column->bv_allnulls = false;
      91                 :        2960 :                 PG_RETURN_BOOL(true);
      92                 :             :         }
      93                 :             : 
      94                 :             :         /*
      95                 :             :          * Otherwise, need to compare the new value with the existing boundaries
      96                 :             :          * and update them accordingly.  First check if it's less than the
      97                 :             :          * existing minimum.
      98                 :             :          */
      99                 :      168704 :         cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     100                 :             :                                                                                  BTLessStrategyNumber);
     101                 :      168704 :         compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[0]);
     102         [ +  + ]:      168704 :         if (DatumGetBool(compar))
     103                 :             :         {
     104         [ +  + ]:         180 :                 if (!attr->attbyval)
     105                 :         110 :                         pfree(DatumGetPointer(column->bv_values[0]));
     106                 :         180 :                 column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
     107                 :         180 :                 updated = true;
     108                 :         180 :         }
     109                 :             : 
     110                 :             :         /*
     111                 :             :          * And now compare it to the existing maximum.
     112                 :             :          */
     113                 :      168704 :         cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     114                 :             :                                                                                  BTGreaterStrategyNumber);
     115                 :      168704 :         compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[1]);
     116         [ +  + ]:      168704 :         if (DatumGetBool(compar))
     117                 :             :         {
     118         [ +  + ]:       85018 :                 if (!attr->attbyval)
     119                 :       79996 :                         pfree(DatumGetPointer(column->bv_values[1]));
     120                 :       85018 :                 column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
     121                 :       85018 :                 updated = true;
     122                 :       85018 :         }
     123                 :             : 
     124                 :      168704 :         PG_RETURN_BOOL(updated);
     125                 :      171664 : }
     126                 :             : 
     127                 :             : /*
     128                 :             :  * Given an index tuple corresponding to a certain page range and a scan key,
     129                 :             :  * return whether the scan key is consistent with the index tuple's min/max
     130                 :             :  * values.  Return true if so, false otherwise.
     131                 :             :  *
     132                 :             :  * We're no longer dealing with NULL keys in the consistent function, that is
     133                 :             :  * now handled by the AM code. That means we should not get any all-NULL ranges
     134                 :             :  * either, because those can't be consistent with regular (not [IS] NULL) keys.
     135                 :             :  */
     136                 :             : Datum
     137                 :       17522 : brin_minmax_consistent(PG_FUNCTION_ARGS)
     138                 :             : {
     139                 :       17522 :         BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     140                 :       17522 :         BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     141                 :       17522 :         ScanKey         key = (ScanKey) PG_GETARG_POINTER(2);
     142                 :       17522 :         Oid                     colloid = PG_GET_COLLATION(),
     143                 :             :                                 subtype;
     144                 :       17522 :         AttrNumber      attno;
     145                 :       17522 :         Datum           value;
     146                 :       17522 :         Datum           matches;
     147                 :       17522 :         FmgrInfo   *finfo;
     148                 :             : 
     149                 :             :         /* This opclass uses the old signature with only three arguments. */
     150         [ +  - ]:       17522 :         Assert(PG_NARGS() == 3);
     151                 :             : 
     152                 :             :         /* Should not be dealing with all-NULL ranges. */
     153         [ +  - ]:       17522 :         Assert(!column->bv_allnulls);
     154                 :             : 
     155                 :       17522 :         attno = key->sk_attno;
     156                 :       17522 :         subtype = key->sk_subtype;
     157                 :       17522 :         value = key->sk_argument;
     158   [ +  +  +  - ]:       17522 :         switch (key->sk_strategy)
     159                 :             :         {
     160                 :             :                 case BTLessStrategyNumber:
     161                 :             :                 case BTLessEqualStrategyNumber:
     162                 :       14002 :                         finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     163                 :        7001 :                                                                                                  key->sk_strategy);
     164                 :       14002 :                         matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
     165                 :        7001 :                                                                                 value);
     166                 :        7001 :                         break;
     167                 :             :                 case BTEqualStrategyNumber:
     168                 :             : 
     169                 :             :                         /*
     170                 :             :                          * In the equality case (WHERE col = someval), we want to return
     171                 :             :                          * the current page range if the minimum value in the range <=
     172                 :             :                          * scan key, and the maximum value >= scan key.
     173                 :             :                          */
     174                 :        3121 :                         finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     175                 :             :                                                                                                  BTLessEqualStrategyNumber);
     176                 :        6242 :                         matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
     177                 :        3121 :                                                                                 value);
     178         [ +  + ]:        3121 :                         if (!DatumGetBool(matches))
     179                 :        1574 :                                 break;
     180                 :             :                         /* max() >= scankey */
     181                 :        1547 :                         finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     182                 :             :                                                                                                  BTGreaterEqualStrategyNumber);
     183                 :        3094 :                         matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
     184                 :        1547 :                                                                                 value);
     185                 :        1547 :                         break;
     186                 :             :                 case BTGreaterEqualStrategyNumber:
     187                 :             :                 case BTGreaterStrategyNumber:
     188                 :       14800 :                         finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     189                 :        7400 :                                                                                                  key->sk_strategy);
     190                 :       14800 :                         matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
     191                 :        7400 :                                                                                 value);
     192                 :        7400 :                         break;
     193                 :             :                 default:
     194                 :             :                         /* shouldn't happen */
     195   [ #  #  #  # ]:           0 :                         elog(ERROR, "invalid strategy number %d", key->sk_strategy);
     196                 :           0 :                         matches = 0;
     197                 :           0 :                         break;
     198                 :             :         }
     199                 :             : 
     200                 :       35044 :         PG_RETURN_DATUM(matches);
     201                 :       17522 : }
     202                 :             : 
     203                 :             : /*
     204                 :             :  * Given two BrinValues, update the first of them as a union of the summary
     205                 :             :  * values contained in both.  The second one is untouched.
     206                 :             :  */
     207                 :             : Datum
     208                 :           0 : brin_minmax_union(PG_FUNCTION_ARGS)
     209                 :             : {
     210                 :           0 :         BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     211                 :           0 :         BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
     212                 :           0 :         BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
     213                 :           0 :         Oid                     colloid = PG_GET_COLLATION();
     214                 :           0 :         AttrNumber      attno;
     215                 :           0 :         Form_pg_attribute attr;
     216                 :           0 :         FmgrInfo   *finfo;
     217                 :           0 :         bool            needsadj;
     218                 :             : 
     219         [ #  # ]:           0 :         Assert(col_a->bv_attno == col_b->bv_attno);
     220         [ #  # ]:           0 :         Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
     221                 :             : 
     222                 :           0 :         attno = col_a->bv_attno;
     223                 :           0 :         attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     224                 :             : 
     225                 :             :         /* Adjust minimum, if B's min is less than A's min */
     226                 :           0 :         finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     227                 :             :                                                                                  BTLessStrategyNumber);
     228                 :           0 :         needsadj = DatumGetBool(FunctionCall2Coll(finfo, colloid, col_b->bv_values[0],
     229                 :           0 :                                                                                           col_a->bv_values[0]));
     230         [ #  # ]:           0 :         if (needsadj)
     231                 :             :         {
     232         [ #  # ]:           0 :                 if (!attr->attbyval)
     233                 :           0 :                         pfree(DatumGetPointer(col_a->bv_values[0]));
     234                 :           0 :                 col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
     235                 :           0 :                                                                                 attr->attbyval, attr->attlen);
     236                 :           0 :         }
     237                 :             : 
     238                 :             :         /* Adjust maximum, if B's max is greater than A's max */
     239                 :           0 :         finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     240                 :             :                                                                                  BTGreaterStrategyNumber);
     241                 :           0 :         needsadj = DatumGetBool(FunctionCall2Coll(finfo, colloid, col_b->bv_values[1],
     242                 :           0 :                                                                                           col_a->bv_values[1]));
     243         [ #  # ]:           0 :         if (needsadj)
     244                 :             :         {
     245         [ #  # ]:           0 :                 if (!attr->attbyval)
     246                 :           0 :                         pfree(DatumGetPointer(col_a->bv_values[1]));
     247                 :           0 :                 col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
     248                 :           0 :                                                                                 attr->attbyval, attr->attlen);
     249                 :           0 :         }
     250                 :             : 
     251                 :           0 :         PG_RETURN_VOID();
     252                 :           0 : }
     253                 :             : 
     254                 :             : /*
     255                 :             :  * Cache and return the procedure for the given strategy.
     256                 :             :  *
     257                 :             :  * Note: this function mirrors inclusion_get_strategy_procinfo; see notes
     258                 :             :  * there.  If changes are made here, see that function too.
     259                 :             :  */
     260                 :             : static FmgrInfo *
     261                 :      356477 : minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
     262                 :             :                                                          uint16 strategynum)
     263                 :             : {
     264                 :      356477 :         MinmaxOpaque *opaque;
     265                 :             : 
     266         [ +  - ]:      356477 :         Assert(strategynum >= 1 &&
     267                 :             :                    strategynum <= BTMaxStrategyNumber);
     268                 :             : 
     269                 :      356477 :         opaque = (MinmaxOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
     270                 :             : 
     271                 :             :         /*
     272                 :             :          * We cache the procedures for the previous subtype in the opaque struct,
     273                 :             :          * to avoid repetitive syscache lookups.  If the subtype changed,
     274                 :             :          * invalidate all the cached entries.
     275                 :             :          */
     276         [ +  + ]:      356477 :         if (opaque->cached_subtype != subtype)
     277                 :             :         {
     278                 :         383 :                 uint16          i;
     279                 :             : 
     280         [ +  + ]:        2298 :                 for (i = 1; i <= BTMaxStrategyNumber; i++)
     281                 :        1915 :                         opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
     282                 :         383 :                 opaque->cached_subtype = subtype;
     283                 :         383 :         }
     284                 :             : 
     285         [ +  + ]:      356477 :         if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
     286                 :             :         {
     287                 :         621 :                 Form_pg_attribute attr;
     288                 :         621 :                 HeapTuple       tuple;
     289                 :         621 :                 Oid                     opfamily,
     290                 :             :                                         oprid;
     291                 :             : 
     292                 :         621 :                 opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
     293                 :         621 :                 attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     294                 :        1242 :                 tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
     295                 :         621 :                                                                 ObjectIdGetDatum(attr->atttypid),
     296                 :         621 :                                                                 ObjectIdGetDatum(subtype),
     297                 :         621 :                                                                 UInt16GetDatum(strategynum));
     298                 :             : 
     299         [ +  - ]:         621 :                 if (!HeapTupleIsValid(tuple))
     300   [ #  #  #  # ]:           0 :                         elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
     301                 :             :                                  strategynum, attr->atttypid, subtype, opfamily);
     302                 :             : 
     303                 :         621 :                 oprid = DatumGetObjectId(SysCacheGetAttrNotNull(AMOPSTRATEGY, tuple,
     304                 :             :                                                                                                                 Anum_pg_amop_amopopr));
     305                 :         621 :                 ReleaseSysCache(tuple);
     306         [ +  - ]:         621 :                 Assert(RegProcedureIsValid(oprid));
     307                 :             : 
     308                 :        1242 :                 fmgr_info_cxt(get_opcode(oprid),
     309                 :         621 :                                           &opaque->strategy_procinfos[strategynum - 1],
     310                 :         621 :                                           bdesc->bd_context);
     311                 :         621 :         }
     312                 :             : 
     313                 :      712954 :         return &opaque->strategy_procinfos[strategynum - 1];
     314                 :      356477 : }
        

Generated by: LCOV version 2.3.2-1