LCOV - code coverage report
Current view: top level - src/backend/access/brin - brin_minmax.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 107 110 97.3 %
Date: 2024-12-03 07:14:52 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * brin_minmax.c
       3             :  *      Implementation of Min/Max opclass for BRIN
       4             :  *
       5             :  * Portions Copyright (c) 1996-2024, 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       41058 : brin_minmax_opcinfo(PG_FUNCTION_ARGS)
      35             : {
      36       41058 :     Oid         typoid = PG_GETARG_OID(0);
      37             :     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       41058 :     result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
      45             :                      sizeof(MinmaxOpaque));
      46       41058 :     result->oi_nstored = 2;
      47       41058 :     result->oi_regular_nulls = true;
      48       41058 :     result->oi_opaque = (MinmaxOpaque *)
      49       41058 :         MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
      50       41058 :     result->oi_typcache[0] = result->oi_typcache[1] =
      51       41058 :         lookup_type_cache(typoid, 0);
      52             : 
      53       41058 :     PG_RETURN_POINTER(result);
      54             : }
      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      828640 : brin_minmax_add_value(PG_FUNCTION_ARGS)
      65             : {
      66      828640 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
      67      828640 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
      68      828640 :     Datum       newval = PG_GETARG_DATUM(2);
      69      828640 :     bool        isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3);
      70      828640 :     Oid         colloid = PG_GET_COLLATION();
      71             :     FmgrInfo   *cmpFn;
      72             :     Datum       compar;
      73      828640 :     bool        updated = false;
      74             :     Form_pg_attribute attr;
      75             :     AttrNumber  attno;
      76             : 
      77             :     Assert(!isnull);
      78             : 
      79      828640 :     attno = column->bv_attno;
      80      828640 :     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      828640 :     if (column->bv_allnulls)
      87             :     {
      88       20880 :         column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
      89       20880 :         column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
      90       20880 :         column->bv_allnulls = false;
      91       20880 :         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      807760 :     cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     100             :                                          BTLessStrategyNumber);
     101      807760 :     compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[0]);
     102      807760 :     if (DatumGetBool(compar))
     103             :     {
     104        1746 :         if (!attr->attbyval)
     105        1326 :             pfree(DatumGetPointer(column->bv_values[0]));
     106        1746 :         column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
     107        1746 :         updated = true;
     108             :     }
     109             : 
     110             :     /*
     111             :      * And now compare it to the existing maximum.
     112             :      */
     113      807760 :     cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     114             :                                          BTGreaterStrategyNumber);
     115      807760 :     compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[1]);
     116      807760 :     if (DatumGetBool(compar))
     117             :     {
     118      333994 :         if (!attr->attbyval)
     119         844 :             pfree(DatumGetPointer(column->bv_values[1]));
     120      333994 :         column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
     121      333994 :         updated = true;
     122             :     }
     123             : 
     124      807760 :     PG_RETURN_BOOL(updated);
     125             : }
     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      105132 : brin_minmax_consistent(PG_FUNCTION_ARGS)
     138             : {
     139      105132 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     140      105132 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     141      105132 :     ScanKey     key = (ScanKey) PG_GETARG_POINTER(2);
     142      105132 :     Oid         colloid = PG_GET_COLLATION(),
     143             :                 subtype;
     144             :     AttrNumber  attno;
     145             :     Datum       value;
     146             :     Datum       matches;
     147             :     FmgrInfo   *finfo;
     148             : 
     149             :     /* This opclass uses the old signature with only three arguments. */
     150             :     Assert(PG_NARGS() == 3);
     151             : 
     152             :     /* Should not be dealing with all-NULL ranges. */
     153             :     Assert(!column->bv_allnulls);
     154             : 
     155      105132 :     attno = key->sk_attno;
     156      105132 :     subtype = key->sk_subtype;
     157      105132 :     value = key->sk_argument;
     158      105132 :     switch (key->sk_strategy)
     159             :     {
     160       42006 :         case BTLessStrategyNumber:
     161             :         case BTLessEqualStrategyNumber:
     162       42006 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     163       42006 :                                                  key->sk_strategy);
     164       42006 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
     165             :                                         value);
     166       42006 :             break;
     167       18726 :         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       18726 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     175             :                                                  BTLessEqualStrategyNumber);
     176       18726 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
     177             :                                         value);
     178       18726 :             if (!DatumGetBool(matches))
     179        9444 :                 break;
     180             :             /* max() >= scankey */
     181        9282 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     182             :                                                  BTGreaterEqualStrategyNumber);
     183        9282 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
     184             :                                         value);
     185        9282 :             break;
     186       44400 :         case BTGreaterEqualStrategyNumber:
     187             :         case BTGreaterStrategyNumber:
     188       44400 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     189       44400 :                                                  key->sk_strategy);
     190       44400 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
     191             :                                         value);
     192       44400 :             break;
     193           0 :         default:
     194             :             /* shouldn't happen */
     195           0 :             elog(ERROR, "invalid strategy number %d", key->sk_strategy);
     196             :             matches = 0;
     197             :             break;
     198             :     }
     199             : 
     200      105132 :     PG_RETURN_DATUM(matches);
     201             : }
     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           8 : brin_minmax_union(PG_FUNCTION_ARGS)
     209             : {
     210           8 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     211           8 :     BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
     212           8 :     BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
     213           8 :     Oid         colloid = PG_GET_COLLATION();
     214             :     AttrNumber  attno;
     215             :     Form_pg_attribute attr;
     216             :     FmgrInfo   *finfo;
     217             :     bool        needsadj;
     218             : 
     219             :     Assert(col_a->bv_attno == col_b->bv_attno);
     220             :     Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
     221             : 
     222           8 :     attno = col_a->bv_attno;
     223           8 :     attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     224             : 
     225             :     /* Adjust minimum, if B's min is less than A's min */
     226           8 :     finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     227             :                                          BTLessStrategyNumber);
     228          16 :     needsadj = FunctionCall2Coll(finfo, colloid, col_b->bv_values[0],
     229           8 :                                  col_a->bv_values[0]);
     230           8 :     if (needsadj)
     231             :     {
     232           6 :         if (!attr->attbyval)
     233           4 :             pfree(DatumGetPointer(col_a->bv_values[0]));
     234           6 :         col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
     235           6 :                                         attr->attbyval, attr->attlen);
     236             :     }
     237             : 
     238             :     /* Adjust maximum, if B's max is greater than A's max */
     239           8 :     finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     240             :                                          BTGreaterStrategyNumber);
     241          16 :     needsadj = FunctionCall2Coll(finfo, colloid, col_b->bv_values[1],
     242           8 :                                  col_a->bv_values[1]);
     243           8 :     if (needsadj)
     244             :     {
     245           8 :         if (!attr->attbyval)
     246           4 :             pfree(DatumGetPointer(col_a->bv_values[1]));
     247           8 :         col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
     248           8 :                                         attr->attbyval, attr->attlen);
     249             :     }
     250             : 
     251           8 :     PG_RETURN_VOID();
     252             : }
     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     1729950 : minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
     262             :                              uint16 strategynum)
     263             : {
     264             :     MinmaxOpaque *opaque;
     265             : 
     266             :     Assert(strategynum >= 1 &&
     267             :            strategynum <= BTMaxStrategyNumber);
     268             : 
     269     1729950 :     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     1729950 :     if (opaque->cached_subtype != subtype)
     277             :     {
     278             :         uint16      i;
     279             : 
     280       15084 :         for (i = 1; i <= BTMaxStrategyNumber; i++)
     281       12570 :             opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
     282        2514 :         opaque->cached_subtype = subtype;
     283             :     }
     284             : 
     285     1729950 :     if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
     286             :     {
     287             :         Form_pg_attribute attr;
     288             :         HeapTuple   tuple;
     289             :         Oid         opfamily,
     290             :                     oprid;
     291             : 
     292        4158 :         opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
     293        4158 :         attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     294        4158 :         tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
     295             :                                 ObjectIdGetDatum(attr->atttypid),
     296             :                                 ObjectIdGetDatum(subtype),
     297             :                                 Int16GetDatum(strategynum));
     298             : 
     299        4158 :         if (!HeapTupleIsValid(tuple))
     300           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
     301             :                  strategynum, attr->atttypid, subtype, opfamily);
     302             : 
     303        4158 :         oprid = DatumGetObjectId(SysCacheGetAttrNotNull(AMOPSTRATEGY, tuple,
     304             :                                                         Anum_pg_amop_amopopr));
     305        4158 :         ReleaseSysCache(tuple);
     306             :         Assert(RegProcedureIsValid(oprid));
     307             : 
     308        4158 :         fmgr_info_cxt(get_opcode(oprid),
     309        4158 :                       &opaque->strategy_procinfos[strategynum - 1],
     310             :                       bdesc->bd_context);
     311             :     }
     312             : 
     313     1729950 :     return &opaque->strategy_procinfos[strategynum - 1];
     314             : }

Generated by: LCOV version 1.14