Support the old signature of BRIN consistent function
authorTomas Vondra <[email protected]>
Fri, 26 Mar 2021 12:17:56 +0000 (13:17 +0100)
committerTomas Vondra <[email protected]>
Fri, 26 Mar 2021 12:17:58 +0000 (13:17 +0100)
Commit a1c649d889 changed the signature of the BRIN consistent function
by adding a new required parameter.  Treating the parameter as optional,
which would make the change backwards incompatibile, was rejected with
the justification that there are few out-of-core extensions, so it's not
worth adding making the code more complex, and it's better to deal with
that in the extension.

But after further thought, that would be rather problematic, because
pg_upgrade simply dumps catalog contents and the same version of an
extension needs to work on both PostgreSQL versions. Supporting both
variants of the consistent function (with 3 or 4 arguments) makes that
possible.

The signature is not the only thing that changed, as commit 72ccf55cb9
moved handling of IS [NOT] NULL keys from the support procedures. But
this change is backward compatible - handling the keys in exension is
unnecessary, but harmless. The consistent function will do a bit of
unnecessary work, but it should be very cheap.

This also undoes most of the changes to the existing opclasses (minmax
and inclusion), making them use the old signature again. This should
make backpatching simpler.

Catversion bump, because of changes in pg_amproc.

Author: Tomas Vondra <[email protected]>
Author: Nikita Glukhov <[email protected]>
Reviewed-by: Mark Dilger <[email protected]>
Reviewed-by: Alexander Korotkov <[email protected]>
Reviewed-by: Masahiko Sawada <[email protected]>
Reviewed-by: John Naylor <[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 078f51bb55af79e9057eb559f44756eb7208c6a9..4f15081674ba284c64298d866231d87cfc7a5c85 100644 (file)
@@ -476,6 +476,19 @@ typedef struct BrinOpcInfo
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><function>bool consistent(BrinDesc *bdesc, BrinValues *column,
+       ScanKey key)</function></term>
+    <listitem>
+     <para>
+      Returns whether the ScanKey is consistent with the given indexed
+      values for a range.
+      The attribute number to use is passed as part of the scan key.
+      This is an older backward-compatible variant of the consistent function.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><function>bool addValue(BrinDesc *bdesc, BrinValues *column,
        Datum newval, bool isnull)</function></term>
index 0bf25fd05bdc37831b26cc90e13dad4b2fd29b21..2d8759c6c1a8efed7ba4b4cb0b7f1e019658dd81 100644 (file)
@@ -634,26 +634,58 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
                        break;
                    }
 
+                   /*
+                    * Collation from the first key (has to be the same for
+                    * all keys for the same attribue).
+                    */
+                   collation = keys[attno - 1][0]->sk_collation;
+
                    /*
                     * 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.
                     *
-                    * XXX We simply use the collation from the first key (it
-                    * has to be the same for all keys for the same attribue).
+                    * The opclass may or may not support processing of multiple
+                    * scan keys. We can determine that based on the number of
+                    * arguments - functions with extra parameter (number of scan
+                    * keys) do support this, otherwise we have to simply pass the
+                    * scan keys one by one.
                     */
-                   collation = keys[attno - 1][0]->sk_collation;
-
-                   /* Check all keys at once */
-                   add = FunctionCall4Coll(&consistentFn[attno - 1],
-                                           collation,
-                                           PointerGetDatum(bdesc),
-                                           PointerGetDatum(bval),
-                                           PointerGetDatum(keys[attno - 1]),
-                                           Int32GetDatum(nkeys[attno - 1]));
-                   addrange = DatumGetBool(add);
-                   if (!addrange)
-                       break;
+                   if (consistentFn[attno - 1].fn_nargs >= 4)
+                   {
+                       /* Check all keys at once */
+                       add = FunctionCall4Coll(&consistentFn[attno - 1],
+                                               collation,
+                                               PointerGetDatum(bdesc),
+                                               PointerGetDatum(bval),
+                                               PointerGetDatum(keys[attno - 1]),
+                                               Int32GetDatum(nkeys[attno - 1]));
+                       addrange = DatumGetBool(add);
+                   }
+                   else
+                   {
+                       /*
+                        * Check keys one by one
+                        *
+                        * 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.
+                        */
+                       int         keyno;
+
+                       for (keyno = 0; keyno < nkeys[attno - 1]; keyno++)
+                       {
+                           add = FunctionCall3Coll(&consistentFn[attno - 1],
+                                                   keys[attno - 1][keyno]->sk_collation,
+                                                   PointerGetDatum(bdesc),
+                                                   PointerGetDatum(bval),
+                                                   PointerGetDatum(keys[attno - 1][keyno]));
+                           addrange = DatumGetBool(add);
+                           if (!addrange)
+                               break;
+                       }
+                   }
                }
            }
        }
index d8cfc9d9098a8e084ffc62451ab774d89aa42f9f..0b384c0bd1eff1220c06b712f45f2a2e1191512f 100644 (file)
@@ -85,8 +85,6 @@ 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);
 
 
 /*
@@ -243,9 +241,9 @@ 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.
+ * We're no longer dealing with NULL keys in the consistent function, that is
+ * now handled by the AM code. That means we should not get any all-NULL ranges
+ * either, because those can't be consistent with regular (not [IS] NULL) keys.
  *
  * All of the strategies are optional.
  */
@@ -254,63 +252,29 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
 {
    BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
    BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
-   ScanKey    *keys = (ScanKey *) PG_GETARG_POINTER(2);
-   int         nkeys = PG_GETARG_INT32(3);
-   Oid         colloid = PG_GET_COLLATION();
-   int         keyno;
+   ScanKey     key = (ScanKey) PG_GETARG_POINTER(2);
+   Oid         colloid = PG_GET_COLLATION(),
+               subtype;
+   Datum       unionval;
+   AttrNumber  attno;
+   Datum       query;
+   FmgrInfo   *finfo;
+   Datum       result;
 
-   /* make sure we got some scan keys */
-   Assert((nkeys > 0) && (keys != NULL));
+   /* This opclass uses the old signature with only three arguments. */
+   Assert(PG_NARGS() == 3);
 
-   /*
-    * 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);
+   /* Should not be dealing with all-NULL ranges. */
+   Assert(!column->bv_allnulls);
 
    /* It has to be checked, if it contains elements that are not mergeable. */
    if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
        PG_RETURN_BOOL(true);
 
-   /* Check that the range is consistent with all regular scan keys. */
-   for (keyno = 0; keyno < nkeys; keyno++)
-   {
-       ScanKey     key = keys[keyno];
-
-       /* NULL keys are handled and filtered-out in bringetbitmap */
-       Assert(!(key->sk_flags & SK_ISNULL));
-
-       /*
-        * 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));
-
+   attno = key->sk_attno;
+   subtype = key->sk_subtype;
+   query = key->sk_argument;
+   unionval = column->bv_values[INCLUSION_UNION];
    switch (key->sk_strategy)
    {
            /*
@@ -330,49 +294,49 @@ inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key,
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTOverRightStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           return !DatumGetBool(result);
+           PG_RETURN_BOOL(!DatumGetBool(result));
 
        case RTOverLeftStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTRightStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           return !DatumGetBool(result);
+           PG_RETURN_BOOL(!DatumGetBool(result));
 
        case RTOverRightStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTLeftStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           return !DatumGetBool(result);
+           PG_RETURN_BOOL(!DatumGetBool(result));
 
        case RTRightStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTOverLeftStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           return !DatumGetBool(result);
+           PG_RETURN_BOOL(!DatumGetBool(result));
 
        case RTBelowStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTOverAboveStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           return !DatumGetBool(result);
+           PG_RETURN_BOOL(!DatumGetBool(result));
 
        case RTOverBelowStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTAboveStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           return !DatumGetBool(result);
+           PG_RETURN_BOOL(!DatumGetBool(result));
 
        case RTOverAboveStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTBelowStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           return !DatumGetBool(result);
+           PG_RETURN_BOOL(!DatumGetBool(result));
 
        case RTAboveStrategyNumber:
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTOverBelowStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           return !DatumGetBool(result);
+           PG_RETURN_BOOL(!DatumGetBool(result));
 
            /*
             * Overlap and contains strategies
@@ -390,7 +354,7 @@ inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key,
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    key->sk_strategy);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           return DatumGetBool(result);
+           PG_RETURN_DATUM(result);
 
            /*
             * Contained by strategies
@@ -410,9 +374,9 @@ inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key,
                                                    RTOverlapStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
            if (DatumGetBool(result))
-               return true;
+               PG_RETURN_BOOL(true);
 
-           return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+           PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
 
            /*
             * Adjacent strategy
@@ -429,12 +393,12 @@ inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key,
                                                    RTOverlapStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
            if (DatumGetBool(result))
-               return true;
+               PG_RETURN_BOOL(true);
 
            finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
                                                    RTAdjacentStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
-           return DatumGetBool(result);
+           PG_RETURN_DATUM(result);
 
            /*
             * Basic comparison strategies
@@ -464,9 +428,9 @@ inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key,
                                                    RTRightStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
            if (!DatumGetBool(result))
-               return true;
+               PG_RETURN_BOOL(true);
 
-           return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+           PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
 
        case RTSameStrategyNumber:
        case RTEqualStrategyNumber:
@@ -474,30 +438,30 @@ inclusion_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key,
                                                    RTContainsStrategyNumber);
            result = FunctionCall2Coll(finfo, colloid, unionval, query);
            if (DatumGetBool(result))
-               return true;
+               PG_RETURN_BOOL(true);
 
-           return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+           PG_RETURN_DATUM(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))
-               return true;
+               PG_RETURN_BOOL(true);
 
-           return DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+           PG_RETURN_DATUM(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);
-           return !DatumGetBool(result);
+           PG_RETURN_BOOL(!DatumGetBool(result));
 
        default:
            /* shouldn't happen */
            elog(ERROR, "invalid strategy number %d", key->sk_strategy);
-           return false;
+           PG_RETURN_BOOL(false);
    }
 }
 
index 032629950f13e0875d926de18f0cae6f5285d8b2..798f06c72220c4d1b26ffa9b4729843dea51d6ea 100644 (file)
@@ -30,8 +30,6 @@ 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
@@ -133,64 +131,32 @@ brin_minmax_add_value(PG_FUNCTION_ARGS)
  * 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.
+ * We're no longer dealing with NULL keys in the consistent function, that is
+ * now handled by the AM code. That means we should not get any all-NULL ranges
+ * either, because those can't be consistent with regular (not [IS] NULL) keys.
  */
 Datum
 brin_minmax_consistent(PG_FUNCTION_ARGS)
 {
    BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
    BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
-   ScanKey    *keys = (ScanKey *) PG_GETARG_POINTER(2);
-   int         nkeys = PG_GETARG_INT32(3);
-   Oid         colloid = PG_GET_COLLATION();
-   int         keyno;
-
-   /* make sure we got some scan keys */
-   Assert((nkeys > 0) && (keys != NULL));
-
-   /*
-    * 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);
-
-   /* Check that the range is consistent with all scan keys. */
-   for (keyno = 0; keyno < nkeys; keyno++)
-   {
-       ScanKey     key = keys[keyno];
-
-       /* NULL keys are handled and filtered-out in bringetbitmap */
-       Assert(!(key->sk_flags & SK_ISNULL));
-
-       /*
-        * 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);
-   }
+   ScanKey     key = (ScanKey) PG_GETARG_POINTER(2);
+   Oid         colloid = PG_GET_COLLATION(),
+               subtype;
+   AttrNumber  attno;
+   Datum       value;
+   Datum       matches;
+   FmgrInfo   *finfo;
 
-   PG_RETURN_DATUM(true);
-}
+   /* This opclass uses the old signature with only three arguments. */
+   Assert(PG_NARGS() == 3);
 
-/*
- * 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;
+   /* Should not be dealing with all-NULL ranges. */
+   Assert(!column->bv_allnulls);
 
+   attno = key->sk_attno;
+   subtype = key->sk_subtype;
+   value = key->sk_argument;
    switch (key->sk_strategy)
    {
        case BTLessStrategyNumber:
@@ -233,7 +199,7 @@ minmax_consistent_key(BrinDesc *bdesc, BrinValues *column, ScanKey key,
            break;
    }
 
-   return DatumGetBool(matches);
+   PG_RETURN_DATUM(matches);
 }
 
 /*
index 2c4f9a3eff26454f8a2a77173405dae538cd55d9..11835d85cd45e28f9bb57d3ee70fa62948da7199 100644 (file)
@@ -97,7 +97,7 @@ brinvalidate(Oid opclassoid)
                break;
            case BRIN_PROCNUM_CONSISTENT:
                ok = check_amproc_signature(procform->amproc, BOOLOID, true,
-                                           4, 4, INTERNALOID, INTERNALOID,
+                                           3, 4, INTERNALOID, INTERNALOID,
                                            INTERNALOID, INT4OID);
                break;
            case BRIN_PROCNUM_UNION:
index 872f0445fa7623204c3e466b799f8099954cd99c..7f8533c93677885da17d791ac5c005602d25c806 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202103261
+#define CATALOG_VERSION_NO 202103262
 
 #endif
index 987ac9140b5b5f2ae543a4d22cbb6bd448a8135a..1662ee93ee31b2f86bf3bcc615a8a0377e7a15d4 100644 (file)
   prosrc => 'brin_minmax_add_value' },
 { oid => '3385', descr => 'BRIN minmax support',
   proname => 'brin_minmax_consistent', prorettype => 'bool',
-  proargtypes => 'internal internal internal int4',
+  proargtypes => 'internal internal internal',
   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 int4',
+  proargtypes => 'internal internal internal',
   prosrc => 'brin_inclusion_consistent' },
 { oid => '4108', descr => 'BRIN inclusion support',
   proname => 'brin_inclusion_union', prorettype => 'bool',