Pass all scan keys to BRIN consistent function at once
authorTomas Vondra <[email protected]>
Mon, 22 Mar 2021 23:12:19 +0000 (00:12 +0100)
committerTomas Vondra <[email protected]>
Mon, 22 Mar 2021 23:45:03 +0000 (00:45 +0100)
This commit changes how we pass scan keys to BRIN consistent function.
Instead of passing them one by one, we now pass all scan keys for a
given attribute at once. That makes the consistent function a bit more
complex, as it has to loop through the keys, but it does allow more
elaborate opclasses that can use multiple keys to eliminate ranges much
more effectively.

The existing BRIN opclasses (minmax, inclusion) don't really benefit
from this change. The primary purpose is to allow future opclases to
benefit from seeing all keys at once.

This does change the BRIN API, because the signature of the consistent
function changes (a new parameter with number of scan keys). So this
breaks existing opclasses, and will require supporting two variants of
the code for different PostgreSQL versions. We've considered supporting
two variants of the consistent, but we've decided not to do that.
Firstly, there's another patch that moves handling of NULL values from
the opclass, which means the opclasses need to be updated anyway.
Secondly, we're not aware of any out-of-core BRIN opclasses, so it does
not seem worth the extra complexity.

Bump catversion, because of pg_proc changes.

Author: Tomas Vondra <[email protected]>
Reviewed-by: Alvaro Herrera <[email protected]>
Reviewed-by: Mark Dilger <[email protected]>
Reviewed-by: Alexander Korotkov <[email protected]>
Reviewed-by: John Naylor <[email protected]>
Reviewed-by: Nikita Glukhov <[email protected]>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com

doc/src/sgml/brin.sgml
src/backend/access/brin/brin.c
src/backend/access/brin/brin_inclusion.c
src/backend/access/brin/brin_minmax.c
src/backend/access/brin/brin_validate.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat

index 06880c0f7bf88a5adf74afbbe727b630489887b4..078f51bb55af79e9057eb559f44756eb7208c6a9 100644 (file)
@@ -464,12 +464,14 @@ typedef struct BrinOpcInfo
 
    <varlistentry>
     <term><function>bool consistent(BrinDesc *bdesc, BrinValues *column,
-       ScanKey key)</function></term>
+       ScanKey *keys, int nkeys)</function></term>
     <listitem>
      <para>
-      Returns whether the ScanKey is consistent with the given indexed
-      values for a range.
+      Returns whether all the ScanKey entries are consistent with the given
+      indexed values for a range.
       The attribute number to use is passed as part of the scan key.
+      Multiple scan keys for the same attribute may be passed at once, the
+      number of entries is determined by the <literal>nkeys</literal> parameter.
      </para>
     </listitem>
    </varlistentry>
index 27ba596c6e47deb0701ad151e4b4be539cd32419..33f4e2c15cf31b4b8a513614ec6c0d31693db58c 100644 (file)
@@ -390,6 +390,9 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
    BrinMemTuple *dtup;
    BrinTuple  *btup = NULL;
    Size        btupsz = 0;
+   ScanKey   **keys;
+   int        *nkeys;
+   int         keyno;
 
    opaque = (BrinOpaque *) scan->opaque;
    bdesc = opaque->bo_bdesc;
@@ -411,6 +414,65 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
     */
    consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts);
 
+   /*
+    * Make room for per-attribute lists of scan keys that we'll pass to the
+    * consistent support procedure. We don't know which attributes have scan
+    * keys, so we allocate space for all attributes. That may use more memory
+    * but it's probably cheaper than determining which attributes are used.
+    *
+    * XXX The widest index can have 32 attributes, so the amount of wasted
+    * memory is negligible. We could invent a more compact approach (with
+    * just space for used attributes) but that would make the matching more
+    * complex so it's not a good trade-off.
+    */
+   keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts);
+   nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts);
+
+   /* Preprocess the scan keys - split them into per-attribute arrays. */
+   for (keyno = 0; keyno < scan->numberOfKeys; keyno++)
+   {
+       ScanKey     key = &scan->keyData[keyno];
+       AttrNumber  keyattno = key->sk_attno;
+
+       /*
+        * The collation of the scan key must match the collation used in the
+        * index column (but only if the search is not IS NULL/ IS NOT NULL).
+        * Otherwise we shouldn't be using this index ...
+        */
+       Assert((key->sk_flags & SK_ISNULL) ||
+              (key->sk_collation ==
+               TupleDescAttr(bdesc->bd_tupdesc,
+                             keyattno - 1)->attcollation));
+
+       /* First time we see this attribute, so init the array of keys. */
+       if (!keys[keyattno - 1])
+       {
+           FmgrInfo   *tmp;
+
+           /*
+            * This is a bit of an overkill - we don't know how many scan keys
+            * are there for this attribute, so we simply allocate the largest
+            * number possible (as if all keys were for this attribute). This
+            * may waste a bit of memory, but we only expect small number of
+            * scan keys in general, so this should be negligible, and
+            * repeated repalloc calls are not free either.
+            */
+           keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);
+
+           /* First time this column, so look up consistent function */
+           Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid);
+
+           tmp = index_getprocinfo(idxRel, keyattno,
+                                   BRIN_PROCNUM_CONSISTENT);
+           fmgr_info_copy(&consistentFn[keyattno - 1], tmp,
+                          CurrentMemoryContext);
+       }
+
+       /* Add key to the per-attribute array. */
+       keys[keyattno - 1][nkeys[keyattno - 1]] = key;
+       nkeys[keyattno - 1]++;
+   }
+
    /* allocate an initial in-memory tuple, out of the per-range memcxt */
    dtup = brin_new_memtuple(bdesc);
 
@@ -471,7 +533,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
            }
            else
            {
-               int         keyno;
+               int         attno;
 
                /*
                 * Compare scan keys with summary values stored for the range.
@@ -481,50 +543,38 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
                 * no keys.
                 */
                addrange = true;
-               for (keyno = 0; keyno < scan->numberOfKeys; keyno++)
+               for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++)
                {
-                   ScanKey     key = &scan->keyData[keyno];
-                   AttrNumber  keyattno = key->sk_attno;
-                   BrinValues *bval = &dtup->bt_columns[keyattno - 1];
+                   BrinValues *bval;
                    Datum       add;
+                   Oid         collation;
 
-                   /*
-                    * The collation of the scan key must match the collation
-                    * used in the index column (but only if the search is not
-                    * IS NULL/ IS NOT NULL).  Otherwise we shouldn't be using
-                    * this index ...
-                    */
-                   Assert((key->sk_flags & SK_ISNULL) ||
-                          (key->sk_collation ==
-                           TupleDescAttr(bdesc->bd_tupdesc,
-                                         keyattno - 1)->attcollation));
-
-                   /* First time this column? look up consistent function */
-                   if (consistentFn[keyattno - 1].fn_oid == InvalidOid)
-                   {
-                       FmgrInfo   *tmp;
-
-                       tmp = index_getprocinfo(idxRel, keyattno,
-                                               BRIN_PROCNUM_CONSISTENT);
-                       fmgr_info_copy(&consistentFn[keyattno - 1], tmp,
-                                      CurrentMemoryContext);
-                   }
+                   /* skip attributes without any scan keys */
+                   if (nkeys[attno - 1] == 0)
+                       continue;
+
+                   bval = &dtup->bt_columns[attno - 1];
+
+                   Assert((nkeys[attno - 1] > 0) &&
+                          (nkeys[attno - 1] <= scan->numberOfKeys));
 
                    /*
                     * Check whether the scan key is consistent with the page
                     * range values; if so, have the pages in the range added
                     * to the output bitmap.
                     *
-                    * When there are multiple scan keys, failure to meet the
-                    * criteria for a single one of them is enough to discard
-                    * the range as a whole, so break out of the loop as soon
-                    * as a false return value is obtained.
+                    * XXX We simply use the collation from the first key (it
+                    * has to be the same for all keys for the same attribue).
                     */
-                   add = FunctionCall3Coll(&consistentFn[keyattno - 1],
-                                           key->sk_collation,
+                   collation = keys[attno - 1][0]->sk_collation;
+
+                   /* Check all keys at once */
+                   add = FunctionCall4Coll(&consistentFn[attno - 1],
+                                           collation,
                                            PointerGetDatum(bdesc),
                                            PointerGetDatum(bval),
-                                           PointerGetDatum(key));
+                                           PointerGetDatum(keys[attno - 1]),
+                                           Int32GetDatum(nkeys[attno - 1]));
                    addrange = DatumGetBool(add);
                    if (!addrange)
                        break;
index 12e5bddd1fc1c9c9e67231d8d5c4125085a382cd..cf2d029048acb0c73f241c1fd647c67f90623fb9 100644 (file)
@@ -85,6 +85,8 @@ static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno,
                                        uint16 procnum);
 static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno,
                                                 Oid subtype, uint16 strategynum);
+static bool inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column,
+                                    ScanKey key, Oid colloid);
 
 
 /*
@@ -251,6 +253,10 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
 /*
  * BRIN inclusion consistent function
  *
+ * We inspect the IS NULL scan keys first, which allows us to make a decision
+ * without looking at the contents of the page range. Only when the page range
+ * matches the IS NULL keys, we check the regular scan keys.
+ *
  * All of the strategies are optional.
  */
 Datum
@@ -258,24 +264,31 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
 {
    BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
    BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
-   ScanKey     key = (ScanKey) PG_GETARG_POINTER(2);
-   Oid         colloid = PG_GET_COLLATION(),
-               subtype;
-   Datum       unionval;
-   AttrNumber  attno;
-   Datum       query;
-   FmgrInfo   *finfo;
-   Datum       result;
-
-   Assert(key->sk_attno == column->bv_attno);
+   ScanKey    *keys = (ScanKey *) PG_GETARG_POINTER(2);
+   int         nkeys = PG_GETARG_INT32(3);
+   Oid         colloid = PG_GET_COLLATION();
+   int         keyno;
+   bool        has_regular_keys = false;
 
-   /* Handle IS NULL/IS NOT NULL tests. */
-   if (key->sk_flags & SK_ISNULL)
+   /* Handle IS NULL/IS NOT NULL tests */
+   for (keyno = 0; keyno < nkeys; keyno++)
    {
+       ScanKey     key = keys[keyno];
+
+       Assert(key->sk_attno == column->bv_attno);
+
+       /* Skip regular scan keys (and remember that we have some). */
+       if ((!key->sk_flags & SK_ISNULL))
+       {
+           has_regular_keys = true;
+           continue;
+       }
+
        if (key->sk_flags & SK_SEARCHNULL)
        {
            if (column->bv_allnulls || column->bv_hasnulls)
-               PG_RETURN_BOOL(true);
+               continue;       /* this key is fine, continue */
+
            PG_RETURN_BOOL(false);
        }
 
@@ -284,7 +297,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
         * only nulls.
         */
        if (key->sk_flags & SK_SEARCHNOTNULL)
-           PG_RETURN_BOOL(!column->bv_allnulls);
+       {
+           if (column->bv_allnulls)
+               PG_RETURN_BOOL(false);
+
+           continue;
+       }
 
        /*
         * Neither IS NULL nor IS NOT NULL was used; assume all indexable
@@ -293,7 +311,14 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
        PG_RETURN_BOOL(false);
    }
 
-   /* If it is all nulls, it cannot possibly be consistent. */
+   /* If there are no regular keys, the page range is considered consistent. */
+   if (!has_regular_keys)
+       PG_RETURN_BOOL(true);
+
+   /*
+    * If is all nulls, it cannot possibly be consistent (at this point we
+    * know there are at least some regular scan keys).
+    */
    if (column->bv_allnulls)
        PG_RETURN_BOOL(false);
 
@@ -301,10 +326,45 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
    if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
        PG_RETURN_BOOL(true);
 
-   attno = key->sk_attno;
-   subtype = key->sk_subtype;
-   query = key->sk_argument;
-   unionval = column->bv_values[INCLUSION_UNION];
+   /* Check that the range is consistent with all regular scan keys. */
+   for (keyno = 0; keyno < nkeys; keyno++)
+   {
+       ScanKey     key = keys[keyno];
+
+       /* Skip IS NULL/IS NOT NULL keys (already handled above). */
+       if (key->sk_flags & SK_ISNULL)
+           continue;
+
+       /*
+        * When there are multiple scan keys, failure to meet the criteria for
+        * a single one of them is enough to discard the range as a whole, so
+        * break out of the loop as soon as a false return value is obtained.
+        */
+       if (!inclusion_consistent_key(bdesc, column, key, colloid))
+           PG_RETURN_BOOL(false);
+   }
+
+   PG_RETURN_BOOL(true);
+}
+
+/*
+ * inclusion_consistent_key
+ *     Determine if the range is consistent with a single scan key.
+ */
+static bool
+inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key,
+                        Oid colloid)
+{
+   FmgrInfo   *finfo;
+   AttrNumber  attno = key->sk_attno;
+   Oid         subtype = key->sk_subtype;
+   Datum       query = key->sk_argument;
+   Datum       unionval = column->bv_values[INCLUSION_UNION];
+   Datum       result;
+
+   /* This should be called only for regular keys, not for IS [NOT] NULL. */
+   Assert(!(key->sk_flags & SK_ISNULL));
+
    switch (key->sk_strategy)
    {
            /*
@@ -324,49 +384,49 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTOverRightStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_BOOL(!DatumGetBool(result));
+           return !DatumGetBool(result);
 
        case RTOverLeftStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTRightStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_BOOL(!DatumGetBool(result));
+           return !DatumGetBool(result);
 
        case RTOverRightStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTLeftStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_BOOL(!DatumGetBool(result));
+           return !DatumGetBool(result);
 
        case RTRightStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTOverLeftStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_BOOL(!DatumGetBool(result));
+           return !DatumGetBool(result);
 
        case RTBelowStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTOverAboveStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_BOOL(!DatumGetBool(result));
+           return !DatumGetBool(result);
 
        case RTOverBelowStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTAboveStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_BOOL(!DatumGetBool(result));
+           return !DatumGetBool(result);
 
        case RTOverAboveStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTBelowStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_BOOL(!DatumGetBool(result));
+           return !DatumGetBool(result);
 
        case RTAboveStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTOverBelowStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_BOOL(!DatumGetBool(result));
+           return !DatumGetBool(result);
 
            /*
             * Overlap and contains strategies
@@ -384,7 +444,7 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    key->sk_strategy);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_DATUM(result);
+           return DatumGetBool(result);
 
            /*
             * Contained by strategies
@@ -404,9 +464,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
                                                    RTOverlapStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
            if (DatumGetBool(result))
-               PG_RETURN_BOOL(true);
+               return true;
 
-           PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+           return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
 
            /*
             * Adjacent strategy
@@ -423,12 +483,12 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
                                                    RTOverlapStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
            if (DatumGetBool(result))
-               PG_RETURN_BOOL(true);
+               return true;
 
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTAdjacentStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_DATUM(result);
+           return DatumGetBool(result);
 
            /*
             * Basic comparison strategies
@@ -458,9 +518,9 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
                                                    RTRightStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
            if (!DatumGetBool(result))
-               PG_RETURN_BOOL(true);
+               return true;
 
-           PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+           return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
 
        case RTSameStrategyNumber:
        case RTEqualStrategyNumber:
@@ -468,30 +528,30 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
                                                    RTContainsStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
            if (DatumGetBool(result))
-               PG_RETURN_BOOL(true);
+               return true;
 
-           PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+           return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
 
        case RTGreaterEqualStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTLeftStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
            if (!DatumGetBool(result))
-               PG_RETURN_BOOL(true);
+               return true;
 
-           PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+           return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
 
        case RTGreaterStrategyNumber:
            /* no need to check for empty elements */
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTLeftStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           PG_RETURN_BOOL(!DatumGetBool(result));
+           return !DatumGetBool(result);
 
        default:
            /* shouldn't happen */
            elog(ERROR, "invalid strategy number %d", key->sk_strategy);
-           PG_RETURN_BOOL(false);
+           return false;
    }
 }
 
index 2ffbd9bf0ddba6640d0e8a5ea5d89195b1a47c67..e116084a02105231b96a5e536d1641615b0c9a82 100644 (file)
@@ -30,6 +30,8 @@ typedef struct MinmaxOpaque
 
 static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno,
                                              Oid subtype, uint16 strategynum);
+static bool minmax_consistent_key(BrinDesc *bdesc, BrinValues *column,
+                                 ScanKey key, Oid colloid);
 
 
 Datum
@@ -140,29 +142,41 @@ brin_minmax_add_value(PG_FUNCTION_ARGS)
  * Given an index tuple corresponding to a certain page range and a scan key,
  * return whether the scan key is consistent with the index tuple's min/max
  * values.  Return true if so, false otherwise.
+ *
+ * We inspect the IS NULL scan keys first, which allows us to make a decision
+ * without looking at the contents of the page range. Only when the page range
+ * matches all those keys, we check the regular scan keys.
  */
 Datum
 brin_minmax_consistent(PG_FUNCTION_ARGS)
 {
    BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
    BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
-   ScanKey     key = (ScanKey) PG_GETARG_POINTER(2);
-   Oid         colloid = PG_GET_COLLATION(),
-               subtype;
-   AttrNumber  attno;
-   Datum       value;
-   Datum       matches;
-   FmgrInfo   *finfo;
-
-   Assert(key->sk_attno == column->bv_attno);
+   ScanKey    *keys = (ScanKey *) PG_GETARG_POINTER(2);
+   int         nkeys = PG_GETARG_INT32(3);
+   Oid         colloid = PG_GET_COLLATION();
+   int         keyno;
+   bool        has_regular_keys = false;
 
    /* handle IS NULL/IS NOT NULL tests */
-   if (key->sk_flags & SK_ISNULL)
+   for (keyno = 0; keyno < nkeys; keyno++)
    {
+       ScanKey     key = keys[keyno];
+
+       Assert(key->sk_attno == column->bv_attno);
+
+       /* Skip regular scan keys (and remember that we have some). */
+       if ((!key->sk_flags & SK_ISNULL))
+       {
+           has_regular_keys = true;
+           continue;
+       }
+
        if (key->sk_flags & SK_SEARCHNULL)
        {
            if (column->bv_allnulls || column->bv_hasnulls)
-               PG_RETURN_BOOL(true);
+               continue;       /* this key is fine, continue */
+
            PG_RETURN_BOOL(false);
        }
 
@@ -171,7 +185,12 @@ brin_minmax_consistent(PG_FUNCTION_ARGS)
         * only nulls.
         */
        if (key->sk_flags & SK_SEARCHNOTNULL)
-           PG_RETURN_BOOL(!column->bv_allnulls);
+       {
+           if (column->bv_allnulls)
+               PG_RETURN_BOOL(false);
+
+           continue;
+       }
 
        /*
         * Neither IS NULL nor IS NOT NULL was used; assume all indexable
@@ -180,13 +199,52 @@ brin_minmax_consistent(PG_FUNCTION_ARGS)
        PG_RETURN_BOOL(false);
    }
 
-   /* if the range is all empty, it cannot possibly be consistent */
+   /* If there are no regular keys, the page range is considered consistent. */
+   if (!has_regular_keys)
+       PG_RETURN_BOOL(true);
+
+   /*
+    * If is all nulls, it cannot possibly be consistent (at this point we
+    * know there are at least some regular scan keys).
+    */
    if (column->bv_allnulls)
        PG_RETURN_BOOL(false);
 
-   attno = key->sk_attno;
-   subtype = key->sk_subtype;
-   value = key->sk_argument;
+   /* Check that the range is consistent with all scan keys. */
+   for (keyno = 0; keyno < nkeys; keyno++)
+   {
+       ScanKey     key = keys[keyno];
+
+       /* ignore IS NULL/IS NOT NULL tests handled above */
+       if (key->sk_flags & SK_ISNULL)
+           continue;
+
+       /*
+        * When there are multiple scan keys, failure to meet the criteria for
+        * a single one of them is enough to discard the range as a whole, so
+        * break out of the loop as soon as a false return value is obtained.
+        */
+       if (!minmax_consistent_key(bdesc, column, key, colloid))
+           PG_RETURN_DATUM(false);;
+   }
+
+   PG_RETURN_DATUM(true);
+}
+
+/*
+ * minmax_consistent_key
+ *     Determine if the range is consistent with a single scan key.
+ */
+static bool
+minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key,
+                     Oid colloid)
+{
+   FmgrInfo   *finfo;
+   AttrNumber  attno = key->sk_attno;
+   Oid         subtype = key->sk_subtype;
+   Datum       value = key->sk_argument;
+   Datum       matches;
+
    switch (key->sk_strategy)
    {
        case BTLessStrategyNumber:
@@ -229,7 +287,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS)
            break;
    }
 
-   PG_RETURN_DATUM(matches);
+   return DatumGetBool(matches);
 }
 
 /*
index 6d4253c05e2050ed6cb331743dece0e87f9634bd..2c4f9a3eff26454f8a2a77173405dae538cd55d9 100644 (file)
@@ -97,8 +97,8 @@ brinvalidate(Oid opclassoid)
                break;
            case BRIN_PROCNUM_CONSISTENT:
                ok = check_amproc_signature(procform->amproc, BOOLOID, true,
-                                           3, 3, INTERNALOID, INTERNALOID,
-                                           INTERNALOID);
+                                           4, 4, INTERNALOID, INTERNALOID,
+                                           INTERNALOID, INT4OID);
                break;
            case BRIN_PROCNUM_UNION:
                ok = check_amproc_signature(procform->amproc, BOOLOID, true,
index c831b55bf9e2fc2a62e688abdb720b1017ae340d..3cf93fd381b5f3d5d0ec91926f5b84b05762260c 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202103191
+#define CATALOG_VERSION_NO 202103231
 
 #endif
index e259531f60bc93144136abd3f0fa791905d5cd56..b9f4afba050308afe58948fb8db048239ebcef1c 100644 (file)
   prosrc => 'brin_minmax_add_value' },
 { oid => '3385', descr => 'BRIN minmax support',
   proname => 'brin_minmax_consistent', prorettype => 'bool',
-  proargtypes => 'internal internal internal',
+  proargtypes => 'internal internal internal int4',
   prosrc => 'brin_minmax_consistent' },
 { oid => '3386', descr => 'BRIN minmax support',
   proname => 'brin_minmax_union', prorettype => 'bool',
   prosrc => 'brin_inclusion_add_value' },
 { oid => '4107', descr => 'BRIN inclusion support',
   proname => 'brin_inclusion_consistent', prorettype => 'bool',
-  proargtypes => 'internal internal internal',
+  proargtypes => 'internal internal internal int4',
   prosrc => 'brin_inclusion_consistent' },
 { oid => '4108', descr => 'BRIN inclusion support',
   proname => 'brin_inclusion_union', prorettype => 'bool',