LCOV - code coverage report
Current view: top level - src/backend/access/brin - brin_minmax.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 96 136 70.6 %
Date: 2020-06-03 09:06:53 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-2020, 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       26706 : brin_minmax_opcinfo(PG_FUNCTION_ARGS)
      37             : {
      38       26706 :     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       26706 :     result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
      47             :                      sizeof(MinmaxOpaque));
      48       26706 :     result->oi_nstored = 2;
      49       26706 :     result->oi_opaque = (MinmaxOpaque *)
      50       26706 :         MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
      51       26706 :     result->oi_typcache[0] = result->oi_typcache[1] =
      52       26706 :         lookup_type_cache(typoid, 0);
      53             : 
      54       26706 :     PG_RETURN_POINTER(result);
      55             : }
      56             : 
      57             : /*
      58             :  * Examine the given index tuple (which contains partial status of a certain
      59             :  * page range) by comparing it to the given value that comes from another heap
      60             :  * tuple.  If the new value is outside the min/max range specified by the
      61             :  * existing tuple values, update the index tuple and return true.  Otherwise,
      62             :  * return false and do not modify in this case.
      63             :  */
      64             : Datum
      65      328946 : brin_minmax_add_value(PG_FUNCTION_ARGS)
      66             : {
      67      328946 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
      68      328946 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
      69      328946 :     Datum       newval = PG_GETARG_DATUM(2);
      70      328946 :     bool        isnull = PG_GETARG_DATUM(3);
      71      328946 :     Oid         colloid = PG_GET_COLLATION();
      72             :     FmgrInfo   *cmpFn;
      73             :     Datum       compar;
      74      328946 :     bool        updated = false;
      75             :     Form_pg_attribute attr;
      76             :     AttrNumber  attno;
      77             : 
      78             :     /*
      79             :      * If the new value is null, we record that we saw it if it's the first
      80             :      * one; otherwise, there's nothing to do.
      81             :      */
      82      328946 :     if (isnull)
      83             :     {
      84        3168 :         if (column->bv_hasnulls)
      85        2304 :             PG_RETURN_BOOL(false);
      86             : 
      87         864 :         column->bv_hasnulls = true;
      88         864 :         PG_RETURN_BOOL(true);
      89             :     }
      90             : 
      91      325778 :     attno = column->bv_attno;
      92      325778 :     attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
      93             : 
      94             :     /*
      95             :      * If the recorded value is null, store the new value (which we know to be
      96             :      * not null) as both minimum and maximum, and we're done.
      97             :      */
      98      325778 :     if (column->bv_allnulls)
      99             :     {
     100       11350 :         column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
     101       11350 :         column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
     102       11350 :         column->bv_allnulls = false;
     103       11350 :         PG_RETURN_BOOL(true);
     104             :     }
     105             : 
     106             :     /*
     107             :      * Otherwise, need to compare the new value with the existing boundaries
     108             :      * and update them accordingly.  First check if it's less than the
     109             :      * existing minimum.
     110             :      */
     111      314428 :     cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     112             :                                          BTLessStrategyNumber);
     113      314428 :     compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[0]);
     114      314428 :     if (DatumGetBool(compar))
     115             :     {
     116         516 :         if (!attr->attbyval)
     117         424 :             pfree(DatumGetPointer(column->bv_values[0]));
     118         516 :         column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
     119         516 :         updated = true;
     120             :     }
     121             : 
     122             :     /*
     123             :      * And now compare it to the existing maximum.
     124             :      */
     125      314428 :     cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     126             :                                          BTGreaterStrategyNumber);
     127      314428 :     compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[1]);
     128      314428 :     if (DatumGetBool(compar))
     129             :     {
     130      210024 :         if (!attr->attbyval)
     131         248 :             pfree(DatumGetPointer(column->bv_values[1]));
     132      210024 :         column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
     133      210024 :         updated = true;
     134             :     }
     135             : 
     136      314428 :     PG_RETURN_BOOL(updated);
     137             : }
     138             : 
     139             : /*
     140             :  * Given an index tuple corresponding to a certain page range and a scan key,
     141             :  * return whether the scan key is consistent with the index tuple's min/max
     142             :  * values.  Return true if so, false otherwise.
     143             :  */
     144             : Datum
     145       70800 : brin_minmax_consistent(PG_FUNCTION_ARGS)
     146             : {
     147       70800 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     148       70800 :     BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
     149       70800 :     ScanKey     key = (ScanKey) PG_GETARG_POINTER(2);
     150       70800 :     Oid         colloid = PG_GET_COLLATION(),
     151             :                 subtype;
     152             :     AttrNumber  attno;
     153             :     Datum       value;
     154             :     Datum       matches;
     155             :     FmgrInfo   *finfo;
     156             : 
     157             :     Assert(key->sk_attno == column->bv_attno);
     158             : 
     159             :     /* handle IS NULL/IS NOT NULL tests */
     160       70800 :     if (key->sk_flags & SK_ISNULL)
     161             :     {
     162         800 :         if (key->sk_flags & SK_SEARCHNULL)
     163             :         {
     164         400 :             if (column->bv_allnulls || column->bv_hasnulls)
     165          28 :                 PG_RETURN_BOOL(true);
     166         372 :             PG_RETURN_BOOL(false);
     167             :         }
     168             : 
     169             :         /*
     170             :          * For IS NOT NULL, we can only skip ranges that are known to have
     171             :          * only nulls.
     172             :          */
     173         400 :         if (key->sk_flags & SK_SEARCHNOTNULL)
     174         400 :             PG_RETURN_BOOL(!column->bv_allnulls);
     175             : 
     176             :         /*
     177             :          * Neither IS NULL nor IS NOT NULL was used; assume all indexable
     178             :          * operators are strict and return false.
     179             :          */
     180           0 :         PG_RETURN_BOOL(false);
     181             :     }
     182             : 
     183             :     /* if the range is all empty, it cannot possibly be consistent */
     184       70000 :     if (column->bv_allnulls)
     185           0 :         PG_RETURN_BOOL(false);
     186             : 
     187       70000 :     attno = key->sk_attno;
     188       70000 :     subtype = key->sk_subtype;
     189       70000 :     value = key->sk_argument;
     190       70000 :     switch (key->sk_strategy)
     191             :     {
     192       28000 :         case BTLessStrategyNumber:
     193             :         case BTLessEqualStrategyNumber:
     194       28000 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     195       28000 :                                                  key->sk_strategy);
     196       28000 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
     197             :                                         value);
     198       28000 :             break;
     199       12400 :         case BTEqualStrategyNumber:
     200             : 
     201             :             /*
     202             :              * In the equality case (WHERE col = someval), we want to return
     203             :              * the current page range if the minimum value in the range <=
     204             :              * scan key, and the maximum value >= scan key.
     205             :              */
     206       12400 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     207             :                                                  BTLessEqualStrategyNumber);
     208       12400 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
     209             :                                         value);
     210       12400 :             if (!DatumGetBool(matches))
     211        6296 :                 break;
     212             :             /* max() >= scankey */
     213        6104 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     214             :                                                  BTGreaterEqualStrategyNumber);
     215        6104 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
     216             :                                         value);
     217        6104 :             break;
     218       29600 :         case BTGreaterEqualStrategyNumber:
     219             :         case BTGreaterStrategyNumber:
     220       29600 :             finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
     221       29600 :                                                  key->sk_strategy);
     222       29600 :             matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
     223             :                                         value);
     224       29600 :             break;
     225           0 :         default:
     226             :             /* shouldn't happen */
     227           0 :             elog(ERROR, "invalid strategy number %d", key->sk_strategy);
     228             :             matches = 0;
     229             :             break;
     230             :     }
     231             : 
     232       70000 :     PG_RETURN_DATUM(matches);
     233             : }
     234             : 
     235             : /*
     236             :  * Given two BrinValues, update the first of them as a union of the summary
     237             :  * values contained in both.  The second one is untouched.
     238             :  */
     239             : Datum
     240           0 : brin_minmax_union(PG_FUNCTION_ARGS)
     241             : {
     242           0 :     BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
     243           0 :     BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
     244           0 :     BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
     245           0 :     Oid         colloid = PG_GET_COLLATION();
     246             :     AttrNumber  attno;
     247             :     Form_pg_attribute attr;
     248             :     FmgrInfo   *finfo;
     249             :     bool        needsadj;
     250             : 
     251             :     Assert(col_a->bv_attno == col_b->bv_attno);
     252             : 
     253             :     /* Adjust "hasnulls" */
     254           0 :     if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
     255           0 :         col_a->bv_hasnulls = true;
     256             : 
     257             :     /* If there are no values in B, there's nothing left to do */
     258           0 :     if (col_b->bv_allnulls)
     259           0 :         PG_RETURN_VOID();
     260             : 
     261           0 :     attno = col_a->bv_attno;
     262           0 :     attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     263             : 
     264             :     /*
     265             :      * Adjust "allnulls".  If A doesn't have values, just copy the values from
     266             :      * B into A, and we're done.  We cannot run the operators in this case,
     267             :      * because values in A might contain garbage.  Note we already established
     268             :      * that B contains values.
     269             :      */
     270           0 :     if (col_a->bv_allnulls)
     271             :     {
     272           0 :         col_a->bv_allnulls = false;
     273           0 :         col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
     274           0 :                                         attr->attbyval, attr->attlen);
     275           0 :         col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
     276           0 :                                         attr->attbyval, attr->attlen);
     277           0 :         PG_RETURN_VOID();
     278             :     }
     279             : 
     280             :     /* Adjust minimum, if B's min is less than A's min */
     281           0 :     finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     282             :                                          BTLessStrategyNumber);
     283           0 :     needsadj = FunctionCall2Coll(finfo, colloid, col_b->bv_values[0],
     284           0 :                                  col_a->bv_values[0]);
     285           0 :     if (needsadj)
     286             :     {
     287           0 :         if (!attr->attbyval)
     288           0 :             pfree(DatumGetPointer(col_a->bv_values[0]));
     289           0 :         col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
     290           0 :                                         attr->attbyval, attr->attlen);
     291             :     }
     292             : 
     293             :     /* Adjust maximum, if B's max is greater than A's max */
     294           0 :     finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
     295             :                                          BTGreaterStrategyNumber);
     296           0 :     needsadj = FunctionCall2Coll(finfo, colloid, col_b->bv_values[1],
     297           0 :                                  col_a->bv_values[1]);
     298           0 :     if (needsadj)
     299             :     {
     300           0 :         if (!attr->attbyval)
     301           0 :             pfree(DatumGetPointer(col_a->bv_values[1]));
     302           0 :         col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
     303           0 :                                         attr->attbyval, attr->attlen);
     304             :     }
     305             : 
     306           0 :     PG_RETURN_VOID();
     307             : }
     308             : 
     309             : /*
     310             :  * Cache and return the procedure for the given strategy.
     311             :  *
     312             :  * Note: this function mirrors inclusion_get_strategy_procinfo; see notes
     313             :  * there.  If changes are made here, see that function too.
     314             :  */
     315             : static FmgrInfo *
     316      704960 : minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
     317             :                              uint16 strategynum)
     318             : {
     319             :     MinmaxOpaque *opaque;
     320             : 
     321             :     Assert(strategynum >= 1 &&
     322             :            strategynum <= BTMaxStrategyNumber);
     323             : 
     324      704960 :     opaque = (MinmaxOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
     325             : 
     326             :     /*
     327             :      * We cache the procedures for the previous subtype in the opaque struct,
     328             :      * to avoid repetitive syscache lookups.  If the subtype changed,
     329             :      * invalidate all the cached entries.
     330             :      */
     331      704960 :     if (opaque->cached_subtype != subtype)
     332             :     {
     333             :         uint16      i;
     334             : 
     335        7908 :         for (i = 1; i <= BTMaxStrategyNumber; i++)
     336        6590 :             opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
     337        1318 :         opaque->cached_subtype = subtype;
     338             :     }
     339             : 
     340      704960 :     if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
     341             :     {
     342             :         Form_pg_attribute attr;
     343             :         HeapTuple   tuple;
     344             :         Oid         opfamily,
     345             :                     oprid;
     346             :         bool        isNull;
     347             : 
     348        2060 :         opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
     349        2060 :         attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
     350        4120 :         tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
     351        2060 :                                 ObjectIdGetDatum(attr->atttypid),
     352             :                                 ObjectIdGetDatum(subtype),
     353             :                                 Int16GetDatum(strategynum));
     354             : 
     355        2060 :         if (!HeapTupleIsValid(tuple))
     356           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
     357             :                  strategynum, attr->atttypid, subtype, opfamily);
     358             : 
     359        2060 :         oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple,
     360             :                                                  Anum_pg_amop_amopopr, &isNull));
     361        2060 :         ReleaseSysCache(tuple);
     362             :         Assert(!isNull && RegProcedureIsValid(oprid));
     363             : 
     364        2060 :         fmgr_info_cxt(get_opcode(oprid),
     365        2060 :                       &opaque->strategy_procinfos[strategynum - 1],
     366             :                       bdesc->bd_context);
     367             :     }
     368             : 
     369      704960 :     return &opaque->strategy_procinfos[strategynum - 1];
     370             : }

Generated by: LCOV version 1.13