LCOV - differential code coverage report
Current view: top level - src/backend/access/brin - brin_minmax.c (source / functions) Coverage Total Hit UIC UBC GBC GIC GNC CBC ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 75.2 % 117 88 1 28 1 3 2 82 5 1
Current Date: 2023-04-08 15:15:32 Functions: 80.0 % 5 4 1 1 3
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*
       2                 :  * brin_minmax.c
       3                 :  *      Implementation of Min/Max opclass for BRIN
       4                 :  *
       5                 :  * Portions Copyright (c) 1996-2023, 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 CBC       20378 : brin_minmax_opcinfo(PG_FUNCTION_ARGS)
      37                 : {
      38           20378 :     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           20378 :     result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
      47                 :                      sizeof(MinmaxOpaque));
      48           20378 :     result->oi_nstored = 2;
      49           20378 :     result->oi_regular_nulls = true;
      50           20378 :     result->oi_opaque = (MinmaxOpaque *)
      51           20378 :         MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
      52           20378 :     result->oi_typcache[0] = result->oi_typcache[1] =
      53           20378 :         lookup_type_cache(typoid, 0);
      54                 : 
      55           20378 :     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          346142 : brin_minmax_add_value(PG_FUNCTION_ARGS)
      67                 : {
      68          346142 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
      69          346142 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
      70          346142 :     Datum       newval = PG_GETARG_DATUM(2);
      71          346142 :     bool        isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3);
      72          346142 :     Oid         colloid = PG_GET_COLLATION();
      73                 :     FmgrInfo   *cmpFn;
      74                 :     Datum       compar;
      75          346142 :     bool        updated = false;
      76                 :     Form_pg_attribute attr;
      77                 :     AttrNumber  attno;
      78                 : 
      79          346142 :     Assert(!isnull);
      80                 : 
      81          346142 :     attno = column->bv_attno;
      82          346142 :     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          346142 :     if (column->bv_allnulls)
      89                 :     {
      90           10307 :         column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
      91           10307 :         column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
      92           10307 :         column->bv_allnulls = false;
      93           10307 :         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          335835 :     cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     102                 :                                          BTLessStrategyNumber);
     103          335835 :     compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[0]);
     104          335835 :     if (DatumGetBool(compar))
     105                 :     {
     106             537 :         if (!attr->attbyval)
     107             327 :             pfree(DatumGetPointer(column->bv_values[0]));
     108             537 :         column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
     109             537 :         updated = true;
     110                 :     }
     111                 : 
     112                 :     /*
     113                 :      * And now compare it to the existing maximum.
     114                 :      */
     115          335835 :     cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     116                 :                                          BTGreaterStrategyNumber);
     117          335835 :     compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[1]);
     118          335835 :     if (DatumGetBool(compar))
     119                 :     {
     120          144875 :         if (!attr->attbyval)
     121             180 :             pfree(DatumGetPointer(column->bv_values[1]));
     122          144875 :         column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
     123          144875 :         updated = true;
     124                 :     }
     125                 : 
     126          335835 :     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           52566 : brin_minmax_consistent(PG_FUNCTION_ARGS)
     140                 : {
     141           52566 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     142           52566 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     143           52566 :     ScanKey     key = (ScanKey) PG_GETARG_POINTER(2);
     144           52566 :     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           52566 :     Assert(PG_NARGS() == 3);
     153                 : 
     154                 :     /* Should not be dealing with all-NULL ranges. */
     155           52566 :     Assert(!column->bv_allnulls);
     156                 : 
     157           52566 :     attno = key->sk_attno;
     158           52566 :     subtype = key->sk_subtype;
     159           52566 :     value = key->sk_argument;
     160           52566 :     switch (key->sk_strategy)
     161                 :     {
     162           21003 :         case BTLessStrategyNumber:
     163                 :         case BTLessEqualStrategyNumber:
     164           21003 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     165           21003 :                                                  key->sk_strategy);
     166           21003 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
     167                 :                                         value);
     168           21003 :             break;
     169            9363 :         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            9363 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     177                 :                                                  BTLessEqualStrategyNumber);
     178            9363 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
     179                 :                                         value);
     180            9363 :             if (!DatumGetBool(matches))
     181            4722 :                 break;
     182                 :             /* max() >= scankey */
     183            4641 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     184                 :                                                  BTGreaterEqualStrategyNumber);
     185            4641 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
     186                 :                                         value);
     187            4641 :             break;
     188           22200 :         case BTGreaterEqualStrategyNumber:
     189                 :         case BTGreaterStrategyNumber:
     190           22200 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     191           22200 :                                                  key->sk_strategy);
     192           22200 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
     193                 :                                         value);
     194           22200 :             break;
     195 UBC           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 CBC       52566 :     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 UBC           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               0 :     Assert(col_a->bv_attno == col_b->bv_attno);
     222               0 :     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 CBC      728877 : minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
     264                 :                              uint16 strategynum)
     265                 : {
     266                 :     MinmaxOpaque *opaque;
     267                 : 
     268          728877 :     Assert(strategynum >= 1 &&
     269                 :            strategynum <= BTMaxStrategyNumber);
     270                 : 
     271          728877 :     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          728877 :     if (opaque->cached_subtype != subtype)
     279                 :     {
     280                 :         uint16      i;
     281                 : 
     282            7452 :         for (i = 1; i <= BTMaxStrategyNumber; i++)
     283            6210 :             opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
     284            1242 :         opaque->cached_subtype = subtype;
     285                 :     }
     286                 : 
     287          728877 :     if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
     288                 :     {
     289                 :         Form_pg_attribute attr;
     290                 :         HeapTuple   tuple;
     291                 :         Oid         opfamily,
     292                 :                     oprid;
     293 ECB             : 
     294 CBC        2049 :         opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
     295            2049 :         attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     296 GIC        2049 :         tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
     297                 :                                 ObjectIdGetDatum(attr->atttypid),
     298                 :                                 ObjectIdGetDatum(subtype),
     299                 :                                 Int16GetDatum(strategynum));
     300 ECB             : 
     301 GBC        2049 :         if (!HeapTupleIsValid(tuple))
     302 UIC           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
     303                 :                  strategynum, attr->atttypid, subtype, opfamily);
     304 ECB             : 
     305 GNC        2049 :         oprid = DatumGetObjectId(SysCacheGetAttrNotNull(AMOPSTRATEGY, tuple,
     306                 :                                                         Anum_pg_amop_amopopr));
     307 CBC        2049 :         ReleaseSysCache(tuple);
     308 GNC        2049 :         Assert(RegProcedureIsValid(oprid));
     309 ECB             : 
     310 CBC        2049 :         fmgr_info_cxt(get_opcode(oprid),
     311 GIC        2049 :                       &opaque->strategy_procinfos[strategynum - 1],
     312                 :                       bdesc->bd_context);
     313                 :     }
     314 ECB             : 
     315 GIC      728877 :     return &opaque->strategy_procinfos[strategynum - 1];
     316                 : }
        

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