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

Generated by: LCOV version 1.14