Support the old signature of BRIN consistent function

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 <tomas.vondra@postgresql.org>
Author: Nikita Glukhov <n.gluhov@postgrespro.ru>
Reviewed-by: Mark Dilger <hornschnorter@gmail.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Reviewed-by: Masahiko Sawada <masahiko.sawada@enterprisedb.com>
Reviewed-by: John Naylor <john.naylor@enterprisedb.com>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
This commit is contained in:
Tomas Vondra 2021-03-26 13:17:56 +01:00
parent a68dfa27d4
commit a681e3c107
7 changed files with 121 additions and 146 deletions

View 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>

View 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;
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;
/* 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;
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;
}
}
}
}
}

View 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);
}
}

View 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);
}
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;
ScanKey key = (ScanKey) PG_GETARG_POINTER(2);
Oid colloid = PG_GET_COLLATION(),
subtype;
AttrNumber attno;
Datum value;
Datum matches;
FmgrInfo *finfo;
/* This opclass uses the old signature with only three arguments. */
Assert(PG_NARGS() == 3);
/* 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);
}
/*

View 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:

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202103261
#define CATALOG_VERSION_NO 202103262
#endif

View File

@ -8232,7 +8232,7 @@
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',
@ -8248,7 +8248,7 @@
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',