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

Generated by: LCOV version 1.13