pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feed From: Tomas Vondra <tomas.vondra@2ndquadrant.com>
To: John Naylor <john.naylor@2ndquadrant.com>
Cc: Alvaro Herrera <alvherre@2ndquadrant.com>
Cc: pgsql-hackers <pgsql-hackers@postgresql.org>
Subject: Re: WIP: BRIN multi-range indexes
Date: Sat, 12 Sep 2020 18:13:03 +0200
Message-ID: <20200912161303.z54jctx5dw4srrh3@development> (raw )
In-Reply-To: <CACPNZCs7jso_=Fw72cc-vc=xq0yXanJjstUs2e5+oxHndiGVjw@mail.gmail.com >
References: <20200910183248.eajizgzuypntsjmh@development >
<20200910200137.GA25491@alvherre.pgsql >
<20200911101441.eyrgxkk234mogguq@development >
<CACPNZCv1ndrWGZnAOC3+MtpSgAQ1ciyOjASFKduxMrNG=C_Bkw@mail.gmail.com >
<20200911180510.hjqxdpgfpy3damqn@development >
<CACPNZCs7jso_=Fw72cc-vc=xq0yXanJjstUs2e5+oxHndiGVjw@mail.gmail.com >
On Fri, Sep 11, 2020 at 03:19:58PM -0400, John Naylor wrote:
>On Fri, Sep 11, 2020 at 2:05 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
>> that bad. But yeah, testing and benchmarking it would be nice. Do you
>> plan to test just the minmax-multi opclass, or will you look at the
>> bloom one too?
>
>Yeah, I'll start looking at bloom next week, and I'll include it when
>I do perf testing.
>
OK. Here is a slightly improved version of the patch series, with better
commit messages and comments, and with the two patches tweaking handling
of NULL values merged into one.
As mentioned in my reply to Alvaro, I'm hoping to get the first two
parts (general improvements) committed soon, so that we can focus on the
new opclasses. I now recall why I was postponing pushing those parts
because it's primarily "just" a preparation for the new opclasses. Both
the scan keys and NULL handling tweaks are not going to help existing
opclasses very much, I think.
The NULL-handling might help a bit, but the scan key changes are mostly
irrelevant. So I'm wondering if we should even change the two existing
opclasses, instead of keeping them as they are (the code actually
supports that by checking number of arguments of the constitent
function). Opinions?
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From 2085561ad36af6994a81c1c6001fbd636077ed38 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 12 Sep 2020 15:07:04 +0200
Subject: [PATCH 1/5] Pass all scan keys to BRIN consistent function at once
Passing all scan keys to the BRIN consistent function at once may allow
elimination of additional ranges, which would be impossible when only
passing individual scan keys.
The code continues to support both the original (one scan key at a time)
and new (all scan keys at once) approaches, depending on whether the
consistent function accepts three or four arguments.
This modifies the existing BRIN opclasses (minmax, inclusion) although
those don't really benefit from this change. The primary purpose of this
is to allow more advanced opclases in the future.
Author: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com>
Reviewed-by: Mark Dilger <hornschnorter@gmail.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
---
src/backend/access/brin/brin.c | 150 +++++++++++++++-----
src/backend/access/brin/brin_inclusion.c | 170 +++++++++++++++--------
src/backend/access/brin/brin_minmax.c | 121 +++++++++++-----
src/backend/access/brin/brin_validate.c | 4 +-
src/include/catalog/pg_proc.dat | 4 +-
5 files changed, 324 insertions(+), 125 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 1f72562c60..ccf609e799 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -389,6 +389,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;
@@ -410,6 +413,61 @@ 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.
+ */
+ 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 index attribute, so init as needed. */
+ 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. This may waste
+ * a bit of memory, but we only expect small number of
+ * scan keys in general, so this should be negligible,
+ * and it's cheaper than having to repalloc repeatedly.
+ */
+ 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);
@@ -470,7 +528,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
else
{
- int keyno;
+ int attno;
/*
* Compare scan keys with summary values stored for the range.
@@ -480,51 +538,75 @@ 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;
- /*
- * 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));
+ /* skip attributes without any san keys */
+ if (nkeys[attno - 1] == 0)
+ continue;
- /* First time this column? look up consistent function */
- if (consistentFn[keyattno - 1].fn_oid == InvalidOid)
- {
- FmgrInfo *tmp;
+ bval = &dtup->bt_columns[attno - 1];
- tmp = index_getprocinfo(idxRel, keyattno,
- BRIN_PROCNUM_CONSISTENT);
- fmgr_info_copy(&consistentFn[keyattno - 1], tmp,
- CurrentMemoryContext);
- }
+ 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.
+ * 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,
*/
- add = FunctionCall3Coll(&consistentFn[keyattno - 1],
- key->sk_collation,
- PointerGetDatum(bdesc),
- PointerGetDatum(bval),
- PointerGetDatum(key));
- addrange = DatumGetBool(add);
+ if (consistentFn[attno - 1].fn_nargs >= 4)
+ {
+ Oid collation;
+
+ /*
+ * 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 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;
+ }
+ }
+
if (!addrange)
break;
}
diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c
index 7e380d66ed..853727190c 100644
--- a/src/backend/access/brin/brin_inclusion.c
+++ b/src/backend/access/brin/brin_inclusion.c
@@ -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);
/*
@@ -258,53 +260,109 @@ 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 regular_keys = false;
- /* Handle IS NULL/IS NOT NULL tests. */
- if (key->sk_flags & SK_ISNULL)
+ /*
+ * First check if there are any IS NULL scan keys, and if we're
+ * violating them. In that case we can terminate early, without
+ * inspecting the ranges.
+ */
+ for (keyno = 0; keyno < nkeys; keyno++)
{
- if (key->sk_flags & SK_SEARCHNULL)
+ ScanKey key = keys[keyno];
+
+ Assert(key->sk_attno == column->bv_attno);
+
+ /* handle IS NULL/IS NOT NULL tests */
+ if (key->sk_flags & SK_ISNULL)
{
- if (column->bv_allnulls || column->bv_hasnulls)
- PG_RETURN_BOOL(true);
- PG_RETURN_BOOL(false);
- }
+ if (key->sk_flags & SK_SEARCHNULL)
+ {
+ if (column->bv_allnulls || column->bv_hasnulls)
+ continue; /* this key is fine, continue */
- /*
- * For IS NOT NULL, we can only skip ranges that are known to have
- * only nulls.
- */
- if (key->sk_flags & SK_SEARCHNOTNULL)
- PG_RETURN_BOOL(!column->bv_allnulls);
+ PG_RETURN_BOOL(false);
+ }
- /*
- * Neither IS NULL nor IS NOT NULL was used; assume all indexable
- * operators are strict and return false.
- */
- PG_RETURN_BOOL(false);
+ /*
+ * For IS NOT NULL, we can only skip ranges that are known to have
+ * only nulls.
+ */
+ if (key->sk_flags & SK_SEARCHNOTNULL)
+ {
+ if (column->bv_allnulls)
+ PG_RETURN_BOOL(false);
+
+ continue;
+ }
+
+ /*
+ * Neither IS NULL nor IS NOT NULL was used; assume all indexable
+ * operators are strict and return false.
+ */
+ PG_RETURN_BOOL(false);
+ }
+ else
+ /* note we have regular (non-NULL) scan keys */
+ regular_keys = true;
}
- /* If it is all nulls, it cannot possibly be consistent. */
- if (column->bv_allnulls)
+ /*
+ * If the page range is all nulls, it cannot possibly be consistent if
+ * there are some regular scan keys.
+ */
+ if (column->bv_allnulls && regular_keys)
PG_RETURN_BOOL(false);
+ /* If there are no regular keys, the page range is considered consistent. */
+ if (!regular_keys)
+ PG_RETURN_BOOL(true);
+
/* It has to be checked, if it contains elements that are not mergeable. */
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 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 (!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;
+
switch (key->sk_strategy)
{
/*
@@ -324,49 +382,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
@@ -385,7 +443,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
@@ -406,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
@@ -425,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);
+ RTAdjacentStrategyNumber);
result = FunctionCall2Coll(finfo, colloid, unionval, query);
- PG_RETURN_DATUM(result);
+ return DatumGetBool(result);
/*
* Basic comparison strategies
@@ -460,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:
@@ -470,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;
}
}
diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c
index 4b5d6a7213..a3bb3558ec 100644
--- a/src/backend/access/brin/brin_minmax.c
+++ b/src/backend/access/brin/brin_minmax.c
@@ -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
@@ -146,47 +148,104 @@ 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 regular_keys = false;
- /* handle IS NULL/IS NOT NULL tests */
- if (key->sk_flags & SK_ISNULL)
+ /*
+ * First check if there are any IS NULL scan keys, and if we're
+ * violating them. In that case we can terminate early, without
+ * inspecting the ranges.
+ */
+ for (keyno = 0; keyno < nkeys; keyno++)
{
- if (key->sk_flags & SK_SEARCHNULL)
+ ScanKey key = keys[keyno];
+
+ Assert(key->sk_attno == column->bv_attno);
+
+ /* handle IS NULL/IS NOT NULL tests */
+ if (key->sk_flags & SK_ISNULL)
{
- if (column->bv_allnulls || column->bv_hasnulls)
- PG_RETURN_BOOL(true);
+ if (key->sk_flags & SK_SEARCHNULL)
+ {
+ if (column->bv_allnulls || column->bv_hasnulls)
+ continue; /* this key is fine, continue */
+
+ PG_RETURN_BOOL(false);
+ }
+
+ /*
+ * For IS NOT NULL, we can only skip ranges that are known to have
+ * only nulls.
+ */
+ if (key->sk_flags & SK_SEARCHNOTNULL)
+ {
+ if (column->bv_allnulls)
+ PG_RETURN_BOOL(false);
+
+ continue;
+ }
+
+ /*
+ * Neither IS NULL nor IS NOT NULL was used; assume all indexable
+ * operators are strict and return false.
+ */
PG_RETURN_BOOL(false);
}
+ else
+ /* note we have regular (non-NULL) scan keys */
+ regular_keys = true;
+ }
- /*
- * For IS NOT NULL, we can only skip ranges that are known to have
- * only nulls.
- */
- if (key->sk_flags & SK_SEARCHNOTNULL)
- PG_RETURN_BOOL(!column->bv_allnulls);
+ /*
+ * If the page range is all nulls, it cannot possibly be consistent if
+ * there are some regular scan keys.
+ */
+ if (column->bv_allnulls && regular_keys)
+ PG_RETURN_BOOL(false);
+
+ /* If there are no regular keys, the page range is considered consistent. */
+ if (!regular_keys)
+ PG_RETURN_BOOL(true);
- /*
- * Neither IS NULL nor IS NOT NULL was used; assume all indexable
- * operators are strict and return false.
+ /* 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.
*/
- PG_RETURN_BOOL(false);
+ if (!minmax_consistent_key(bdesc, column, key, colloid))
+ PG_RETURN_DATUM(false);;
}
- /* if the range is all empty, it cannot possibly be consistent */
- if (column->bv_allnulls)
- PG_RETURN_BOOL(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;
- attno = key->sk_attno;
- subtype = key->sk_subtype;
- value = key->sk_argument;
switch (key->sk_strategy)
{
case BTLessStrategyNumber:
@@ -229,7 +288,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS)
break;
}
- PG_RETURN_DATUM(matches);
+ return DatumGetBool(matches);
}
/*
diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c
index fb0615463e..0c28137c8f 100644
--- a/src/backend/access/brin/brin_validate.c
+++ b/src/backend/access/brin/brin_validate.c
@@ -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);
+ 3, 4, INTERNALOID, INTERNALOID,
+ INTERNALOID, INT4OID);
break;
case BRIN_PROCNUM_UNION:
ok = check_amproc_signature(procform->amproc, BOOLOID, true,
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 687509ba92..925b262b60 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8104,7 +8104,7 @@
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',
@@ -8120,7 +8120,7 @@
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',
--
2.25.4
From d7812522e57ebb89aed73af5f5d736e380a8ac1f Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 12 Sep 2020 15:07:28 +0200
Subject: [PATCH 2/5] Move IS [NOT] NULL handling from BRIN support functions
The handling of IS [NOT] NULL clauses is independent of an opclass, and
most of the code was exactly the same in both minmax and inclusion. So
instead move the code from support procedures to the AM methods etc.
This simplifies the code quite a bit, and it simplifies the support
procedures quite a bit, as they don't need to care about NULL values and
flags at all.
Author: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Author: Nikita Glukhov <n.gluhov@postgrespro.ru>
Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com>
Reviewed-by: Mark Dilger <hornschnorter@gmail.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Reviewed-by: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
Reviewed-by: John Naylor <john.naylor@2ndquadrant.com>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
---
src/backend/access/brin/brin.c | 287 +++++++++++++++++------
src/backend/access/brin/brin_inclusion.c | 106 +--------
src/backend/access/brin/brin_minmax.c | 103 +-------
src/include/access/brin_internal.h | 3 +
4 files changed, 235 insertions(+), 264 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index ccf609e799..83c71d0f8e 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -35,6 +35,7 @@
#include "storage/freespace.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
#include "utils/rel.h"
@@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state);
static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
BrinTuple *b);
static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
-
+static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc,
+ BrinMemTuple *dtup, Datum *values, bool *nulls);
+static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys);
/*
* BRIN handler function: return IndexAmRoutine with access method parameters
@@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
OffsetNumber off;
BrinTuple *brtup;
BrinMemTuple *dtup;
- int keyno;
CHECK_FOR_INTERRUPTS();
@@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
dtup = brin_deform_tuple(bdesc, brtup, NULL);
- /*
- * Compare the key values of the new tuple to the stored index values;
- * our deformed tuple will get updated if the new tuple doesn't fit
- * the original range (note this means we can't break out of the loop
- * early). Make a note of whether this happens, so that we know to
- * insert the modified tuple later.
- */
- for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++)
- {
- Datum result;
- BrinValues *bval;
- FmgrInfo *addValue;
-
- bval = &dtup->bt_columns[keyno];
- addValue = index_getprocinfo(idxRel, keyno + 1,
- BRIN_PROCNUM_ADDVALUE);
- result = FunctionCall4Coll(addValue,
- idxRel->rd_indcollation[keyno],
- PointerGetDatum(bdesc),
- PointerGetDatum(bval),
- values[keyno],
- nulls[keyno]);
- /* if that returned true, we need to insert the updated tuple */
- need_insert |= DatumGetBool(result);
- }
+ need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls);
if (!need_insert)
{
@@ -359,6 +337,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys)
return scan;
}
+
/*
* Execute the index scan.
*
@@ -389,8 +368,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
BrinMemTuple *dtup;
BrinTuple *btup = NULL;
Size btupsz = 0;
- ScanKey **keys;
- int *nkeys;
+ ScanKey **keys,
+ **nullkeys;
+ int *nkeys,
+ *nnullkeys;
int keyno;
opaque = (BrinOpaque *) scan->opaque;
@@ -415,10 +396,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
/*
* Make room for per-attribute lists of scan keys that we'll pass to the
- * consistent support procedure.
+ * consistent support procedure. We keep null and regular keys separate,
+ * so that we can easily pass regular keys to the consistent function.
*/
keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts);
+ nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts);
nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts);
+ nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts);
/*
* Preprocess the scan keys - split them into per-attribute arrays.
@@ -439,23 +423,24 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
TupleDescAttr(bdesc->bd_tupdesc,
keyattno - 1)->attcollation));
- /* First time we see this index attribute, so init as needed. */
- if (!keys[keyattno-1])
+ /*
+ * First time we see this index attribute, so init as needed.
+ *
+ * This is a bit of an overkill - we don't know how many scan
+ * keys are there for a given attribute, so we simply allocate
+ * the largest number possible (as if all scan keys belonged to
+ * the same 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 it's probably cheaper than having
+ * to repalloc repeatedly.
+ */
+ if (consistentFn[keyattno - 1].fn_oid == InvalidOid)
{
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. This may waste
- * a bit of memory, but we only expect small number of
- * scan keys in general, so this should be negligible,
- * and it's cheaper than having to repalloc repeatedly.
- */
- keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);
-
- /* First time this column, so look up consistent function */
- Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid);
+ /* No key/null arrays for this attribute. */
+ Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0));
+ Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0));
tmp = index_getprocinfo(idxRel, keyattno,
BRIN_PROCNUM_CONSISTENT);
@@ -463,9 +448,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
CurrentMemoryContext);
}
- /* Add key to the per-attribute array. */
- keys[keyattno - 1][nkeys[keyattno - 1]] = key;
- nkeys[keyattno - 1]++;
+ /* Add key to the proper per-attribute array. */
+ if (key->sk_flags & SK_ISNULL)
+ {
+ if (!nullkeys[keyattno - 1])
+ nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);
+
+ nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key;
+ nnullkeys[keyattno - 1]++;
+ }
+ else
+ {
+ if (!keys[keyattno - 1])
+ keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);
+
+ keys[keyattno - 1][nkeys[keyattno - 1]] = key;
+ nkeys[keyattno - 1]++;
+ }
}
/* allocate an initial in-memory tuple, out of the per-range memcxt */
@@ -552,6 +551,45 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Assert((nkeys[attno - 1] > 0) &&
(nkeys[attno - 1] <= scan->numberOfKeys));
+ /*
+ * First check if there are any IS [NOT] NULL scan keys,
+ * and if we're violating them. In that case we can
+ * terminate early, without invoking the support function.
+ *
+ * As there may be more keys, we can only detemine mismatch
+ * within this loop.
+ */
+ if (bdesc->bd_info[attno - 1]->oi_regular_nulls &&
+ !check_null_keys(bval, nullkeys[attno - 1],
+ nnullkeys[attno - 1]))
+ {
+ /*
+ * If any of the IS [NOT] NULL keys failed, the page
+ * range as a whole can't pass. So terminate the loop.
+ */
+ addrange = false;
+ break;
+ }
+
+ /*
+ * So either there are no IS [NOT] NULL keys, or all passed.
+ * If there are no regular scan keys, we're done - the page
+ * range matches. If there are regular keys, but the page
+ * range is marked as 'all nulls' it can't possibly pass
+ * (we're assuming the operators are strict).
+ */
+
+ /* No regular scan keys - page range as a whole passes. */
+ if (!nkeys[attno - 1])
+ continue;
+
+ /* If it is all nulls, it cannot possibly be consistent. */
+ if (bval->bv_allnulls)
+ {
+ addrange = false;
+ break;
+ }
+
/*
* Check whether the scan key is consistent with the page
* range values; if so, have the pages in the range added
@@ -694,7 +732,6 @@ brinbuildCallback(Relation index,
{
BrinBuildState *state = (BrinBuildState *) brstate;
BlockNumber thisblock;
- int i;
thisblock = ItemPointerGetBlockNumber(tid);
@@ -723,25 +760,8 @@ brinbuildCallback(Relation index,
}
/* Accumulate the current tuple into the running state */
- for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++)
- {
- FmgrInfo *addValue;
- BrinValues *col;
- Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i);
-
- col = &state->bs_dtuple->bt_columns[i];
- addValue = index_getprocinfo(index, i + 1,
- BRIN_PROCNUM_ADDVALUE);
-
- /*
- * Update dtuple state, if and as necessary.
- */
- FunctionCall4Coll(addValue,
- attr->attcollation,
- PointerGetDatum(state->bs_bdesc),
- PointerGetDatum(col),
- values[i], isnull[i]);
- }
+ (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple,
+ values, isnull);
}
/*
@@ -1520,6 +1540,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b)
FmgrInfo *unionFn;
BrinValues *col_a = &a->bt_columns[keyno];
BrinValues *col_b = &db->bt_columns[keyno];
+ BrinOpcInfo *opcinfo = bdesc->bd_info[keyno];
+
+ if (opcinfo->oi_regular_nulls)
+ {
+ /* Adjust "hasnulls". */
+ if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
+ col_a->bv_hasnulls = true;
+
+ /* If there are no values in B, there's nothing left to do. */
+ if (col_b->bv_allnulls)
+ continue;
+
+ /*
+ * Adjust "allnulls". If A doesn't have values, just copy the
+ * values from B into A, and we're done. We cannot run the
+ * operators in this case, because values in A might contain
+ * garbage. Note we already established that B contains values.
+ */
+ if (col_a->bv_allnulls)
+ {
+ int i;
+
+ col_a->bv_allnulls = false;
+
+ for (i = 0; i < opcinfo->oi_nstored; i++)
+ col_a->bv_values[i] =
+ datumCopy(col_b->bv_values[i],
+ opcinfo->oi_typcache[i]->typbyval,
+ opcinfo->oi_typcache[i]->typlen);
+
+ continue;
+ }
+ }
unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1,
BRIN_PROCNUM_UNION);
@@ -1573,3 +1626,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
*/
FreeSpaceMapVacuum(idxrel);
}
+
+static bool
+add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup,
+ Datum *values, bool *nulls)
+{
+ int keyno;
+ bool modified = false;
+
+ /*
+ * Compare the key values of the new tuple to the stored index values;
+ * our deformed tuple will get updated if the new tuple doesn't fit
+ * the original range (note this means we can't break out of the loop
+ * early). Make a note of whether this happens, so that we know to
+ * insert the modified tuple later.
+ */
+ for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++)
+ {
+ Datum result;
+ BrinValues *bval;
+ FmgrInfo *addValue;
+
+ bval = &dtup->bt_columns[keyno];
+
+ if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno])
+ {
+ /*
+ * If the new value is null, we record that we saw it if it's
+ * the first one; otherwise, there's nothing to do.
+ */
+ if (!bval->bv_hasnulls)
+ {
+ bval->bv_hasnulls = true;
+ modified = true;
+ }
+
+ continue;
+ }
+
+ addValue = index_getprocinfo(idxRel, keyno + 1,
+ BRIN_PROCNUM_ADDVALUE);
+ result = FunctionCall4Coll(addValue,
+ idxRel->rd_indcollation[keyno],
+ PointerGetDatum(bdesc),
+ PointerGetDatum(bval),
+ values[keyno],
+ nulls[keyno]);
+ /* if that returned true, we need to insert the updated tuple */
+ modified |= DatumGetBool(result);
+ }
+
+ return modified;
+}
+
+static bool
+check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys)
+{
+ int keyno;
+
+ /*
+ * First check if there are any IS [NOT] NULL scan keys, and if we're
+ * violating them.
+ */
+ for (keyno = 0; keyno < nnullkeys; keyno++)
+ {
+ ScanKey key = nullkeys[keyno];
+
+ Assert(key->sk_attno == bval->bv_attno);
+
+ /* Handle only IS NULL/IS NOT NULL tests */
+ if (!(key->sk_flags & SK_ISNULL))
+ continue;
+
+ if (key->sk_flags & SK_SEARCHNULL)
+ {
+ /* IS NULL scan key, but range has no NULLs */
+ if (!bval->bv_allnulls && !bval->bv_hasnulls)
+ return false;
+ }
+ else if (key->sk_flags & SK_SEARCHNOTNULL)
+ {
+ /*
+ * For IS NOT NULL, we can only skip ranges that are known to
+ * have only nulls.
+ */
+ if (bval->bv_allnulls)
+ return false;
+ }
+ else
+ {
+ /*
+ * Neither IS NULL nor IS NOT NULL was used; assume all indexable
+ * operators are strict and thus return false with NULL value in
+ * the scan key.
+ */
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c
index 853727190c..f5eaef416f 100644
--- a/src/backend/access/brin/brin_inclusion.c
+++ b/src/backend/access/brin/brin_inclusion.c
@@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS)
*/
result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque));
result->oi_nstored = 3;
+ result->oi_regular_nulls = true;
result->oi_opaque = (InclusionOpaque *)
MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
@@ -141,7 +142,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
Datum newval = PG_GETARG_DATUM(2);
- bool isnull = PG_GETARG_BOOL(3);
+ bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3);
Oid colloid = PG_GET_COLLATION();
FmgrInfo *finfo;
Datum result;
@@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
AttrNumber attno;
Form_pg_attribute attr;
- /*
- * If the new value is null, we record that we saw it if it's the first
- * one; otherwise, there's nothing to do.
- */
- if (isnull)
- {
- if (column->bv_hasnulls)
- PG_RETURN_BOOL(false);
-
- column->bv_hasnulls = true;
- PG_RETURN_BOOL(true);
- }
+ Assert(!isnull);
attno = column->bv_attno;
attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
@@ -264,63 +254,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
int nkeys = PG_GETARG_INT32(3);
Oid colloid = PG_GET_COLLATION();
int keyno;
- bool regular_keys = false;
-
- /*
- * First check if there are any IS NULL scan keys, and if we're
- * violating them. In that case we can terminate early, without
- * inspecting the ranges.
- */
- for (keyno = 0; keyno < nkeys; keyno++)
- {
- ScanKey key = keys[keyno];
-
- Assert(key->sk_attno == column->bv_attno);
-
- /* handle IS NULL/IS NOT NULL tests */
- if (key->sk_flags & SK_ISNULL)
- {
- if (key->sk_flags & SK_SEARCHNULL)
- {
- if (column->bv_allnulls || column->bv_hasnulls)
- continue; /* this key is fine, continue */
-
- PG_RETURN_BOOL(false);
- }
-
- /*
- * For IS NOT NULL, we can only skip ranges that are known to have
- * only nulls.
- */
- if (key->sk_flags & SK_SEARCHNOTNULL)
- {
- if (column->bv_allnulls)
- PG_RETURN_BOOL(false);
-
- continue;
- }
-
- /*
- * Neither IS NULL nor IS NOT NULL was used; assume all indexable
- * operators are strict and return false.
- */
- PG_RETURN_BOOL(false);
- }
- else
- /* note we have regular (non-NULL) scan keys */
- regular_keys = true;
- }
-
- /*
- * If the page range is all nulls, it cannot possibly be consistent if
- * there are some regular scan keys.
- */
- if (column->bv_allnulls && regular_keys)
- PG_RETURN_BOOL(false);
-
- /* If there are no regular keys, the page range is considered consistent. */
- if (!regular_keys)
- PG_RETURN_BOOL(true);
/* It has to be checked, if it contains elements that are not mergeable. */
if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
@@ -331,9 +264,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
{
ScanKey key = keys[keyno];
- /* ignore IS NULL/IS NOT NULL tests handled above */
- if (key->sk_flags & SK_ISNULL)
- continue;
+ /* 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
@@ -574,37 +506,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS)
Datum result;
Assert(col_a->bv_attno == col_b->bv_attno);
-
- /* Adjust "hasnulls". */
- if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
- col_a->bv_hasnulls = true;
-
- /* If there are no values in B, there's nothing left to do. */
- if (col_b->bv_allnulls)
- PG_RETURN_VOID();
+ Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
attno = col_a->bv_attno;
attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
- /*
- * Adjust "allnulls". If A doesn't have values, just copy the values from
- * B into A, and we're done. We cannot run the operators in this case,
- * because values in A might contain garbage. Note we already established
- * that B contains values.
- */
- if (col_a->bv_allnulls)
- {
- col_a->bv_allnulls = false;
- col_a->bv_values[INCLUSION_UNION] =
- datumCopy(col_b->bv_values[INCLUSION_UNION],
- attr->attbyval, attr->attlen);
- col_a->bv_values[INCLUSION_UNMERGEABLE] =
- col_b->bv_values[INCLUSION_UNMERGEABLE];
- col_a->bv_values[INCLUSION_CONTAINS_EMPTY] =
- col_b->bv_values[INCLUSION_CONTAINS_EMPTY];
- PG_RETURN_VOID();
- }
-
/* If B includes empty elements, mark A similarly, if needed. */
if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&
DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY]))
diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c
index a3bb3558ec..a625ef7aac 100644
--- a/src/backend/access/brin/brin_minmax.c
+++ b/src/backend/access/brin/brin_minmax.c
@@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS)
result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
sizeof(MinmaxOpaque));
result->oi_nstored = 2;
+ result->oi_regular_nulls = true;
result->oi_opaque = (MinmaxOpaque *)
MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
result->oi_typcache[0] = result->oi_typcache[1] =
@@ -69,7 +70,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS)
BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
Datum newval = PG_GETARG_DATUM(2);
- bool isnull = PG_GETARG_DATUM(3);
+ bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3);
Oid colloid = PG_GET_COLLATION();
FmgrInfo *cmpFn;
Datum compar;
@@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS)
Form_pg_attribute attr;
AttrNumber attno;
- /*
- * If the new value is null, we record that we saw it if it's the first
- * one; otherwise, there's nothing to do.
- */
- if (isnull)
- {
- if (column->bv_hasnulls)
- PG_RETURN_BOOL(false);
-
- column->bv_hasnulls = true;
- PG_RETURN_BOOL(true);
- }
+ Assert(!isnull);
attno = column->bv_attno;
attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
@@ -152,72 +142,14 @@ brin_minmax_consistent(PG_FUNCTION_ARGS)
int nkeys = PG_GETARG_INT32(3);
Oid colloid = PG_GET_COLLATION();
int keyno;
- bool regular_keys = false;
-
- /*
- * First check if there are any IS NULL scan keys, and if we're
- * violating them. In that case we can terminate early, without
- * inspecting the ranges.
- */
- for (keyno = 0; keyno < nkeys; keyno++)
- {
- ScanKey key = keys[keyno];
-
- Assert(key->sk_attno == column->bv_attno);
-
- /* handle IS NULL/IS NOT NULL tests */
- if (key->sk_flags & SK_ISNULL)
- {
- if (key->sk_flags & SK_SEARCHNULL)
- {
- if (column->bv_allnulls || column->bv_hasnulls)
- continue; /* this key is fine, continue */
-
- PG_RETURN_BOOL(false);
- }
-
- /*
- * For IS NOT NULL, we can only skip ranges that are known to have
- * only nulls.
- */
- if (key->sk_flags & SK_SEARCHNOTNULL)
- {
- if (column->bv_allnulls)
- PG_RETURN_BOOL(false);
-
- continue;
- }
-
- /*
- * Neither IS NULL nor IS NOT NULL was used; assume all indexable
- * operators are strict and return false.
- */
- PG_RETURN_BOOL(false);
- }
- else
- /* note we have regular (non-NULL) scan keys */
- regular_keys = true;
- }
-
- /*
- * If the page range is all nulls, it cannot possibly be consistent if
- * there are some regular scan keys.
- */
- if (column->bv_allnulls && regular_keys)
- PG_RETURN_BOOL(false);
-
- /* If there are no regular keys, the page range is considered consistent. */
- if (!regular_keys)
- PG_RETURN_BOOL(true);
/* 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;
+ /* 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
@@ -308,34 +240,11 @@ brin_minmax_union(PG_FUNCTION_ARGS)
bool needsadj;
Assert(col_a->bv_attno == col_b->bv_attno);
-
- /* Adjust "hasnulls" */
- if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
- col_a->bv_hasnulls = true;
-
- /* If there are no values in B, there's nothing left to do */
- if (col_b->bv_allnulls)
- PG_RETURN_VOID();
+ Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
attno = col_a->bv_attno;
attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
- /*
- * Adjust "allnulls". If A doesn't have values, just copy the values from
- * B into A, and we're done. We cannot run the operators in this case,
- * because values in A might contain garbage. Note we already established
- * that B contains values.
- */
- if (col_a->bv_allnulls)
- {
- col_a->bv_allnulls = false;
- col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
- attr->attbyval, attr->attlen);
- col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
- attr->attbyval, attr->attlen);
- PG_RETURN_VOID();
- }
-
/* Adjust minimum, if B's min is less than A's min */
finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 9ffc9100c0..7c4f3da0a0 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -27,6 +27,9 @@ typedef struct BrinOpcInfo
/* Number of columns stored in an index column of this opclass */
uint16 oi_nstored;
+ /* Regular processing of NULLs in BrinValues? */
+ bool oi_regular_nulls;
+
/* Opaque pointer for the opclass' private use */
void *oi_opaque;
--
2.25.4
From 2839692af136e25083b00cba8c98f42411b05098 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 12 Sep 2020 15:07:43 +0200
Subject: [PATCH 3/5] BRIN bloom indexes
Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN
indexes using the new opclasses only allow equality queries, but that
works for data like UUID, MAC addresses etc. for which range queries are
not very common (and the data look random, making BRIN minmax indexes
inefficient anyway).
BRIN bloom opclasses allow specifying the usual Bloom parameters, like
expected number of distinct values (in the BRIN range) and desired
false positive rate.
The opclasses store 32-bit hashes of the values, not the raw indexed
values. This assumes the hash functions for data types has low number
of collisions, good performance etc. Collisions are not a huge issue
though, because the number of values in a BRIN ranges is fairly small.
The summary does not start as a Bloom filter right away - it initially
stores a plain array of hashes. Only when the size of the array grows
too large it switches to proper Bloom filter. This means that ranges
with low number of distinct values the summary will use less space.
Author: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com
---
doc/src/sgml/brin.sgml | 178 ++++
doc/src/sgml/ref/create_index.sgml | 31 +
src/backend/access/brin/Makefile | 1 +
src/backend/access/brin/brin_bloom.c | 1107 +++++++++++++++++++++
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 4 +
src/include/catalog/pg_amop.dat | 170 ++++
src/include/catalog/pg_amproc.dat | 447 +++++++++
src/include/catalog/pg_opclass.dat | 72 ++
src/include/catalog/pg_opfamily.dat | 38 +
src/include/catalog/pg_proc.dat | 34 +
src/include/catalog/pg_type.dat | 7 +
src/test/regress/expected/brin_bloom.out | 456 +++++++++
src/test/regress/expected/opr_sanity.out | 3 +-
src/test/regress/expected/psql.out | 3 +-
src/test/regress/expected/type_sanity.out | 7 +-
src/test/regress/parallel_schedule | 5 +
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/brin_bloom.sql | 404 ++++++++
19 files changed, 2965 insertions(+), 5 deletions(-)
create mode 100644 src/backend/access/brin/brin_bloom.c
create mode 100644 src/test/regress/expected/brin_bloom.out
create mode 100644 src/test/regress/sql/brin_bloom.sql
diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml
index 4420794e5b..577dd18c49 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
</row>
</thead>
<tbody>
+ <row>
+ <entry valign="middle"><literal>int8_bloom_ops</literal></entry>
+ <entry><literal>= (bit,bit)</literal></entry>
+ </row>
<row>
<entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry>
<entry><literal>= (bit,bit)</literal></entry>
@@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>|&> (box,box)</literal></entry></row>
<row><entry><literal>|>> (box,box)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry>
+ <entry><literal>= (character,character)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry>
<entry><literal>= (character,character)</literal></entry>
@@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (character,character)</literal></entry></row>
<row><entry><literal>>= (character,character)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>bytea_bloom_ops</literal></entry>
+ <entry><literal>= (bytea,bytea)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry>
<entry><literal>= (bytea,bytea)</literal></entry>
@@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (bytea,bytea)</literal></entry></row>
<row><entry><literal>>= (bytea,bytea)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>char_bloom_ops</literal></entry>
+ <entry><literal>= ("char","char")</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry>
<entry><literal>= ("char","char")</literal></entry>
@@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> ("char","char")</literal></entry></row>
<row><entry><literal>>= ("char","char")</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>date_bloom_ops</literal></entry>
+ <entry><literal>= (date,date)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry>
<entry><literal>= (date,date)</literal></entry>
@@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (date,date)</literal></entry></row>
<row><entry><literal>>= (date,date)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>float4_bloom_ops</literal></entry>
+ <entry><literal>= (float4,float4)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry>
<entry><literal>= (float4,float4)</literal></entry>
@@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (float4,float4)</literal></entry></row>
<row><entry><literal>>= (float4,float4)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>float8_bloom_ops</literal></entry>
+ <entry><literal>= (float8,float8)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry>
<entry><literal>= (float8,float8)</literal></entry>
@@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>= (inet,inet)</literal></entry></row>
<row><entry><literal>&& (inet,inet)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>inet_bloom_ops</literal></entry>
+ <entry><literal>= (inet,inet)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry>
<entry><literal>= (inet,inet)</literal></entry>
@@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (inet,inet)</literal></entry></row>
<row><entry><literal>>= (inet,inet)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>int2_bloom_ops</literal></entry>
+ <entry><literal>= (int2,int2)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry>
<entry><literal>= (int2,int2)</literal></entry>
@@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (int2,int2)</literal></entry></row>
<row><entry><literal>>= (int2,int2)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>int4_bloom_ops</literal></entry>
+ <entry><literal>= (int4,int4)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry>
<entry><literal>= (int4,int4)</literal></entry>
@@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (int4,int4)</literal></entry></row>
<row><entry><literal>>= (int4,int4)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>int8_bloom_ops</literal></entry>
+ <entry><literal>= (bigint,bigint)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry>
<entry><literal>= (bigint,bigint)</literal></entry>
@@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (bigint,bigint)</literal></entry></row>
<row><entry><literal>>= (bigint,bigint)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>interval_bloom_ops</literal></entry>
+ <entry><literal>= (interval,interval)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry>
<entry><literal>= (interval,interval)</literal></entry>
@@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (interval,interval)</literal></entry></row>
<row><entry><literal>>= (interval,interval)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry>
+ <entry><literal>= (macaddr,macaddr)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry>
<entry><literal>= (macaddr,macaddr)</literal></entry>
@@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (macaddr,macaddr)</literal></entry></row>
<row><entry><literal>>= (macaddr,macaddr)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry>
+ <entry><literal>= (macaddr8,macaddr8)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry>
<entry><literal>= (macaddr8,macaddr8)</literal></entry>
@@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (macaddr8,macaddr8)</literal></entry></row>
<row><entry><literal>>= (macaddr8,macaddr8)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>name_bloom_ops</literal></entry>
+ <entry><literal>= (name,name)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry>
<entry><literal>= (name,name)</literal></entry>
@@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (name,name)</literal></entry></row>
<row><entry><literal>>= (name,name)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>numeric_bloom_ops</literal></entry>
+ <entry><literal>= (numeric,numeric)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry>
<entry><literal>= (numeric,numeric)</literal></entry>
@@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (numeric,numeric)</literal></entry></row>
<row><entry><literal>>= (numeric,numeric)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>oid_bloom_ops</literal></entry>
+ <entry><literal>= (oid,oid)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry>
<entry><literal>= (oid,oid)</literal></entry>
@@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (oid,oid)</literal></entry></row>
<row><entry><literal>>= (oid,oid)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry>
+ <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry>
<entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
@@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>&> (anyrange,anyrange)</literal></entry></row>
<row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>text_bloom_ops</literal></entry>
+ <entry><literal>= (text,text)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry>
<entry><literal>= (text,text)</literal></entry>
@@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (text,text)</literal></entry></row>
<row><entry><literal>>= (text,text)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry>
+ <entry><literal>= (tid,tid)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry>
<entry><literal>= (tid,tid)</literal></entry>
@@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (tid,tid)</literal></entry></row>
<row><entry><literal>>= (tid,tid)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry>
+ <entry><literal>= (timestamp,timestamp)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry>
<entry><literal>= (timestamp,timestamp)</literal></entry>
@@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timestamp,timestamp)</literal></entry></row>
<row><entry><literal>>= (timestamp,timestamp)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry>
+ <entry><literal>= (timestamptz,timestamptz)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry>
<entry><literal>= (timestamptz,timestamptz)</literal></entry>
@@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timestamptz,timestamptz)</literal></entry></row>
<row><entry><literal>>= (timestamptz,timestamptz)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>time_bloom_ops</literal></entry>
+ <entry><literal>= (time,time)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry>
<entry><literal>= (time,time)</literal></entry>
@@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (time,time)</literal></entry></row>
<row><entry><literal>>= (time,time)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>timetz_bloom_ops</literal></entry>
+ <entry><literal>= (timetz,timetz)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry>
<entry><literal>= (timetz,timetz)</literal></entry>
@@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timetz,timetz)</literal></entry></row>
<row><entry><literal>>= (timetz,timetz)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>uuid_bloom_ops</literal></entry>
+ <entry><literal>= (uuid,uuid)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry>
<entry><literal>= (uuid,uuid)</literal></entry>
@@ -779,6 +903,60 @@ typedef struct BrinOpcInfo
function can improve index performance.
</para>
+ <para>
+ To write an operator class for a data type that implements only an equality
+ operator and supports hashing, it is possible to use the bloom support procedures
+ alongside the corresponding operators, as shown in
+ <xref linkend="brin-extensibility-bloom-table"/>.
+ All operator class members (procedures and operators) are mandatory.
+ </para>
+
+ <table id="brin-extensibility-bloom-table">
+ <title>Procedure and Support Numbers for Bloom Operator Classes</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Operator class member</entry>
+ <entry>Object</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Support Procedure 1</entry>
+ <entry>internal function <function>brin_bloom_opcinfo()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 2</entry>
+ <entry>internal function <function>brin_bloom_add_value()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 3</entry>
+ <entry>internal function <function>brin_bloom_consistent()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 4</entry>
+ <entry>internal function <function>brin_bloom_union()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 11</entry>
+ <entry>function to compute hash of an element</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 1</entry>
+ <entry>operator equal-to</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Support procedure numbers 1-10 are reserved for the BRIN internal
+ functions, so the SQL level functions start with number 11. Support
+ function number 11 is the main function required to build the index.
+ It should accept one argument with the same data type as the operator class,
+ and return a hash of the value.
+ </para>
+
<para>
Both minmax and inclusion operator classes support cross-data-type
operators, though with these the dependencies become more complicated.
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 33aa64e81d..9c90d451a7 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>n_distinct_per_range</literal></term>
+ <listitem>
+ <para>
+ Defines the estimated number of distinct non-null values in the block
+ range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the
+ Bloom filter. It behaves similarly to <literal>n_distinct</literal> option
+ for <xref linkend="sql-altertable"/>. When set to a positive value,
+ each block range is assumed to contain this number of distinct non-null
+ values. When set to a negative value, which must be greater than or
+ equal to -1, the number of distinct non-null is assumed linear with
+ the maximum possible number of tuples in the block range (about 290
+ rows per block). The default values is <literal>-0.1</literal>, and
+ the minimum number of distinct non-null values is <literal>128</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>false_positive_rate</literal></term>
+ <listitem>
+ <para>
+ Defines the desired false positive rate used by <acronym>BRIN</acronym>
+ bloom indexes for sizing of the Bloom filter. The values must be be
+ greater than 0.0 and smaller than 1.0. The default values is 0.01,
+ which is 1% false positive rate.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect2>
diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile
index 468e1e289a..6b56131215 100644
--- a/src/backend/access/brin/Makefile
+++ b/src/backend/access/brin/Makefile
@@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = \
brin.o \
+ brin_bloom.o \
brin_inclusion.o \
brin_minmax.o \
brin_pageops.o \
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
new file mode 100644
index 0000000000..c2cbbd9400
--- /dev/null
+++ b/src/backend/access/brin/brin_bloom.c
@@ -0,0 +1,1107 @@
+/*
+ * brin_bloom.c
+ * Implementation of Bloom opclass for BRIN
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * A BRIN opclass summarizing page range into a bloom filter.
+ *
+ * Bloom filters allow efficient test whether a given page range contains
+ * a particular value. Therefore, if we summarize each page range into a
+ * bloom filter, we can easily and cheaply test wheter it containst values
+ * we get later.
+ *
+ * The index only supports equality operator, similarly to hash indexes.
+ * BRIN bloom indexes are however much smaller, and support only bitmap
+ * scans.
+ *
+ * Note: Don't confuse this with bloom indexes, implemented in a contrib
+ * module. That extension implements an entirely new AM, building a bloom
+ * filter on multiple columns in a single row. This opclass works with an
+ * existing AM (BRIN) and builds bloom filter on a column.
+ *
+ *
+ * values vs. hashes
+ * -----------------
+ *
+ * The original column values are not used directly, but are first hashed
+ * using the regular type-specific hash function, producing a uint32 hash.
+ * And this hash value is then added to the summary - either stored as is
+ * (in the sorted mode), or hashed again and added to the bloom filter.
+ *
+ * This allows the code to treat all data types (byval/byref/...) the same
+ * way, with only minimal space requirements. For example we don't need to
+ * store varlena types differently in the sorted mode, etc. Everything is
+ * uint32 making it much simpler.
+ *
+ * Of course, this assumes the built-in hash function is reasonably good,
+ * without too many collisions etc. But that does seem to be the case, at
+ * least based on past experience. After all, the same hash functions are
+ * used for hash indexes, hash partitioning and so on.
+ *
+ *
+ * sizing the bloom filter
+ * -----------------------
+ *
+ * Size of a bloom filter depends on the number of distinct values we will
+ * store in it, and the desired false positive rate. The higher the number
+ * of distinct values and/or the lower the false positive rate, the larger
+ * the bloom filter. On the other hand, we want to keep the index as small
+ * as possible - that's one of the basic advantages of BRIN indexes.
+ *
+ * The number of distinct elements (in a page range) depends on the data,
+ * we can consider it fixed. This simplifies the trade-off to just false
+ * positive rate vs. size.
+ *
+ * At the page range level, false positive rate is a probability the bloom
+ * filter matches a random value. For the whole index (with sufficiently
+ * many page ranges) it represents the fraction of the index ranges (and
+ * thus fraction of the table to be scanned) matching the random value.
+ *
+ * Furthermore, the size of the bloom filter is subject to implementation
+ * limits - it has to fit onto a single index page (8kB by default). As
+ * the bitmap is inherently random, compression can't reliably help here.
+ * To reduce the size of a filter (to fit to a page), we have to either
+ * accept higher false positive rate (undesirable), or reduce the number
+ * of distinct items to be stored in the filter. We can't quite the input
+ * data, of course, but we may make the BRIN page ranges smaller - instead
+ * of the default 128 pages (1MB) we may build index with 16-page ranges,
+ * or something like that. This does help even for random data sets, as
+ * the number of rows per heap page is limited (to ~290 with very narrow
+ * tables, likely ~20 in practice).
+ *
+ * Of course, good sizing decisions depend on having the necessary data,
+ * i.e. number of distinct values in a page range (of a given size) and
+ * table size (to estimate cost change due to change in false positive
+ * rate due to having larger index vs. scanning larger indexes). We may
+ * not have that data - for example when building an index on empty table
+ * it's not really possible. And for some data we only have estimates for
+ * the whole table and we can only estimate per-range values (ndistinct).
+ *
+ * Another challenge is that while the bloom filter is per-column, it's
+ * the whole index tuple that has to fit into a page. And for multi-column
+ * indexes that may include pieces we have no control over (not necessarily
+ * bloom filters, the other columns may use other BRIN opclasses). So it's
+ * not entirely clear how to distrubute the space between those columns.
+ *
+ * The current logic, implemented in brin_bloom_get_ndistinct, attempts to
+ * make some basic sizing decisions, based on the table ndistinct estimate.
+ *
+ *
+ * sort vs. hash
+ * -------------
+ *
+ * As explained in the preceding section, sizing bloom filters correctly is
+ * difficult in practice. It's also true that bloom filters quickly degrade
+ * after exceeding the expected number of distinct items. It's therefore
+ * expected that people will overshoot the parameters a bit (particularly
+ * the ndistinct per page range), perhaps by a factor of 2 or more.
+ *
+ * It's also possible the data set is not uniform - it may have ranges with
+ * very many distinct items per range, but also ranges with only very few
+ * distinct items. The bloom filter has to be sized for the more variable
+ * ranges, making it rather wasteful in the less variable part.
+ *
+ * For example, if some page ranges have 1000 distinct values, that means
+ * about ~1.2kB bloom filter with 1% false positive rate. For page ranges
+ * that only contain 10 distinct values, that's wasteful, because we might
+ * store the values (the uint32 hashes) in just 40B.
+ *
+ * To address these issues, the opclass stores the raw values directly, and
+ * only switches to the actual bloom filter after reaching the same space
+ * requirements.
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/brin/brin_bloom.c
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/brin.h"
+#include "access/brin_internal.h"
+#include "access/brin_tuple.h"
+#include "access/hash.h"
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/stratnum.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_amop.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+#include <math.h>
+
+#define BloomEqualStrategyNumber 1
+
+/*
+ * Additional SQL level support functions
+ *
+ * Procedure numbers must not use values reserved for BRIN itself; see
+ * brin_internal.h.
+ */
+#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */
+#define PROCNUM_HASH 11 /* required */
+
+/*
+ * Subtract this from procnum to obtain index in BloomOpaque arrays
+ * (Must be equal to minimum of private procnums).
+ */
+#define PROCNUM_BASE 11
+
+/*
+ * Flags. We only use a single bit for now, to decide whether we're in the
+ * sorted or hash phase. So we have 15 bits for future use, if needed. The
+ * filters are expected to be hundreds of bytes, so this is negligible.
+ */
+#define BLOOM_FLAG_PHASE_HASH 0x0001
+
+#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH)
+#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f))
+
+/*
+ * Number of hashes to accumulate before deduplicating and sorting in the
+ * sort phase? We want this fairly small to reduce the amount of space and
+ * also speed-up bsearch lookups.
+ */
+#define BLOOM_MAX_UNSORTED 32
+
+/*
+ * Storage type for BRIN's reloptions.
+ */
+typedef struct BloomOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ double nDistinctPerRange; /* number of distinct values per range */
+ double falsePositiveRate; /* false positive for bloom filter */
+} BloomOptions;
+
+/*
+ * The current min value (16) is somewhat arbitrary, but it's based
+ * on the fact that the filter header is ~20B alone, which is about
+ * the same as the filter bitmap for 16 distinct items with 1% false
+ * positive rate. So by allowing lower values we'd not gain much. In
+ * any case, the min should not be larger than MaxHeapTuplesPerPage
+ * (~290), which is the theoretical maximum for single-page ranges.
+ */
+#define BLOOM_MIN_NDISTINCT_PER_RANGE 16
+
+/*
+ * Used to determine number of distinct items, based on the number of rows
+ * in a page range. The 10% is somewhat similar to what estimate_num_groups
+ * does, so we use the same factor here.
+ */
+#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */
+
+/*
+ * The 1% value is mostly arbitrary, it just looks nice.
+ */
+#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */
+
+#define BloomGetNDistinctPerRange(opts) \
+ ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \
+ (((BloomOptions *) (opts))->nDistinctPerRange) : \
+ BLOOM_DEFAULT_NDISTINCT_PER_RANGE)
+
+#define BloomGetFalsePositiveRate(opts) \
+ ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \
+ (((BloomOptions *) (opts))->falsePositiveRate) : \
+ BLOOM_DEFAULT_FALSE_POSITIVE_RATE)
+
+/*
+ * Bloom Filter
+ *
+ * Represents a bloom filter, built on hashes of the indexed values. That is,
+ * we compute a uint32 hash of the value, and then store this hash into the
+ * bloom filter (and compute additional hashes on it).
+ *
+ * We use an optimisation that initially we store the uint32 values directly,
+ * without the extra hashing step. And only later filling the bitmap space,
+ * we switch to the regular bloom filter mode.
+ *
+ * PHASE_SORTED
+ *
+ * Initially we copy the uint32 hash into the bitmap, regularly sorting the
+ * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted
+ * values).
+ *
+ * The idea is that if we only see very few distinct values, we can store
+ * them in less space compared to the (sparse) bloom filter bitmap. It also
+ * stores them exactly, although that's not a big advantage as almost-empty
+ * bloom filter has false positive rate close to zero anyway.
+ *
+ * PHASE_HASH
+ *
+ * Once we fill the bitmap space in the sorted phase, we switch to the hash
+ * phase, where we actually use the bloom filter. We treat the uint32 hashes
+ * as input values, and hash them again with different seeds (to get the k
+ * hash functions needed for bloom filter).
+ *
+ *
+ * XXX Perhaps we could save a few bytes by using different data types, but
+ * considering the size of the bitmap, the difference is negligible.
+ *
+ * XXX We could also implement "sparse" bloom filters, keeping only the
+ * bytes that are not entirely 0. That might make the "sorted" phase
+ * mostly unnecessary.
+ *
+ * XXX We can also watch the number of bits set in the bloom filter, and
+ * then stop using it (and not store the bitmap, to save space) when the
+ * false positive rate gets too high.
+ */
+typedef struct BloomFilter
+{
+ /* varlena header (do not touch directly!) */
+ int32 vl_len_;
+
+ /* space for various flags (phase, etc.) */
+ uint16 flags;
+
+ /* fields used only in the SORTED phase */
+ uint16 nvalues; /* number of hashes stored (total) */
+ uint16 nsorted; /* number of hashes in the sorted part */
+
+ /* fields for the HASHED phase */
+ uint8 nhashes; /* number of hash functions */
+ uint32 nbits; /* number of bits in the bitmap (size) */
+ uint32 nbits_set; /* number of bits set to 1 */
+
+ /* data of the bloom filter (used both for sorted and hashed phase) */
+ char data[FLEXIBLE_ARRAY_MEMBER];
+
+} BloomFilter;
+
+static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter);
+
+
+/*
+ * bloom_init
+ * Initialize the Bloom Filter, allocate all the memory.
+ *
+ * The filter is initialized with optimal size for ndistinct expected
+ * values, and requested false positive rate. The filter is stored as
+ * varlena.
+ */
+static BloomFilter *
+bloom_init(int ndistinct, double false_positive_rate)
+{
+ Size len;
+ BloomFilter *filter;
+
+ int m; /* number of bits */
+ double k; /* number of hash functions */
+
+ Assert(ndistinct > 0);
+ Assert((false_positive_rate > 0) && (false_positive_rate < 1.0));
+
+ m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0)))));
+
+ /* round m to whole bytes */
+ m = ((m + 7) / 8) * 8;
+
+ /*
+ * round(log(2.0) * m / ndistinct), but assume round() may not be
+ * available on Windows
+ */
+ k = log(2.0) * m / ndistinct;
+ k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k);
+
+ /*
+ * Allocate the bloom filter with a minimum size 64B (about 40B in the
+ * bitmap part). We require space at least for the header.
+ *
+ * XXX Maybe the 64B min size is not really needed?
+ */
+ len = Max(offsetof(BloomFilter, data), 64);
+
+ filter = (BloomFilter *) palloc0(len);
+
+ filter->flags = 0; /* implies SORTED phase */
+ filter->nhashes = (int) k;
+ filter->nbits = m;
+
+ SET_VARSIZE(filter, len);
+
+ return filter;
+}
+
+/* simple uint32 comparator, for pg_qsort and bsearch */
+static int
+cmp_uint32(const void *a, const void *b)
+{
+ uint32 *ia = (uint32 *) a;
+ uint32 *ib = (uint32 *) b;
+
+ if (*ia == *ib)
+ return 0;
+ else if (*ia < *ib)
+ return -1;
+ else
+ return 1;
+}
+
+/*
+ * bloom_compact
+ * Compact the filter during the 'sorted' phase.
+ *
+ * We sort the uint32 hashes and remove duplicates, for two main reasons.
+ * Firstly, to keep most of the data sorted for bsearch lookups. Secondly,
+ * we try to save space by removing the duplicates, allowing us to stay
+ * in the sorted phase a bit longer.
+ *
+ * We currently don't repalloc the bitmap, i.e. we don't free the memory
+ * here - in the worst case we waste space for up to 32 unsorted hashes
+ * (if all of them are already in the sorted part), so about 128B. We can
+ * either reduce the number of unsorted items (e.g. to 8 hashes, which
+ * would mean 32B), or start doing the repalloc.
+ *
+ * We do however set the varlena length, to minimize the storage needs.
+ */
+static void
+bloom_compact(BloomFilter *filter)
+{
+ int i, j, k;
+ Size len;
+
+ uint32 *values;
+ uint32 *result;
+ uint32 *sorted;
+ uint32 *unsorted;
+
+ int nvalues;
+ int nsorted;
+ int nunsorted;
+
+ /* never call compact on filters in HASH phase */
+ Assert(BLOOM_IS_SORTED(filter));
+
+ /* when already fully sorted, no chance to compact anything */
+ if (filter->nvalues == filter->nsorted)
+ return;
+
+ values = (uint32 *) filter->data;
+
+ /* result of merging */
+ k = 0;
+ result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues);
+
+ /* first we have sorted data, then unsorted */
+ sorted = values;
+ nsorted = filter->nsorted;
+
+ unsorted = &values[filter->nsorted];
+ nunsorted = filter->nvalues - filter->nsorted;
+
+ /* sort the unsorted part, then merge the two parts */
+ pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32);
+
+ i = 0; /* sorted index */
+ j = 0; /* unsorted index */
+
+ while ((i < nsorted) && (j < nunsorted))
+ {
+ if (sorted[i] <= unsorted[j])
+ result[k++] = sorted[i++];
+ else
+ result[k++] = unsorted[j++];
+ }
+
+ while (i < nsorted)
+ result[k++] = sorted[i++];
+
+ while (j < nunsorted)
+ result[k++] = unsorted[j++];
+
+ /* compact - remove duplicate values */
+ nvalues = 1;
+ for (i = 1; i < filter->nvalues; i++)
+ {
+ /* if different from the last value, keep it */
+ if (result[i] != result[nvalues - 1])
+ result[nvalues++] = result[i];
+ }
+
+ memcpy(filter->data, result, sizeof(uint32) * nvalues);
+
+ filter->nvalues = nvalues;
+ filter->nsorted = nvalues;
+
+ len = offsetof(BloomFilter, data) +
+ (filter->nvalues) * sizeof(uint32);
+
+ SET_VARSIZE(filter, len);
+}
+
+/*
+ * bloom_add_value
+ * Add value to the bloom filter.
+ */
+static BloomFilter *
+bloom_add_value(BloomFilter *filter, uint32 value, bool *updated)
+{
+ int i;
+ uint32 big_h, h, d;
+
+ /* assume 'not updated' by default */
+ Assert(filter);
+
+ /* if we're in the sorted phase, we store the hashes directly */
+ if (BLOOM_IS_SORTED(filter))
+ {
+ /* how many uint32 hashes can we fit into the bitmap */
+ int maxvalues = filter->nbits / (8 * sizeof(uint32));
+
+ /* do not overflow the bitmap space or number of unsorted items */
+ Assert(filter->nvalues <= maxvalues);
+ Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED);
+
+ /*
+ * In this branch we always update the filter - we either add the
+ * hash to the unsorted part, or switch the filter to hashing.
+ */
+ if (updated)
+ *updated = true;
+
+ /*
+ * If the array is full, or if we reached the limit on unsorted
+ * items, try to compact the filter first, before attempting to
+ * add the new value.
+ */
+ if ((filter->nvalues == maxvalues) ||
+ (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED))
+ bloom_compact(filter);
+
+ /*
+ * Can we squeeze one more uint32 hash into the bitmap? Also make
+ * sure there's enough space in the bytea value first.
+ */
+ if (filter->nvalues < maxvalues)
+ {
+ Size len = VARSIZE_ANY(filter);
+ Size need = offsetof(BloomFilter, data) +
+ (filter->nvalues + 1) * sizeof(uint32);
+
+ /*
+ * We don't double the size here, as in the first place we care about
+ * reducing storage requirements, and the doubling happens automatically
+ * in memory contexts (so the repalloc should be cheap in most cases).
+ */
+ if (len < need)
+ {
+ filter = (BloomFilter *) repalloc(filter, need);
+ SET_VARSIZE(filter, need);
+ }
+
+ /* copy the new value into the filter */
+ memcpy(&filter->data[filter->nvalues * sizeof(uint32)],
+ &value, sizeof(uint32));
+
+ filter->nvalues++;
+
+ /* we're done */
+ return filter;
+ }
+
+ /* can't add any more exact hashes, so switch to hashing */
+ filter = bloom_switch_to_hashing(filter);
+ }
+
+ /* we better be in the hashing phase */
+ Assert(BLOOM_IS_HASHED(filter));
+
+ /* compute the hashes, used for the bloom filter */
+ big_h = ((uint32) DatumGetInt64(hash_uint32(value)));
+
+ h = big_h % filter->nbits;
+ d = big_h % (filter->nbits - 1);
+
+ /* compute the requested number of hashes */
+ for (i = 0; i < filter->nhashes; i++)
+ {
+ int byte = (h / 8);
+ int bit = (h % 8);
+
+ /* if the bit is not set, set it and remember we did that */
+ if (! (filter->data[byte] & (0x01 << bit)))
+ {
+ filter->data[byte] |= (0x01 << bit);
+ filter->nbits_set++;
+ if (updated)
+ *updated = true;
+ }
+
+ /* next bit */
+ h += d++;
+ if (h >= filter->nbits)
+ h -= filter->nbits;
+
+ if (d == filter->nbits)
+ d = 0;
+ }
+
+ return filter;
+}
+
+/*
+ * bloom_switch_to_hashing
+ * Switch the bloom filter from sorted to hashing mode.
+ */
+static BloomFilter *
+bloom_switch_to_hashing(BloomFilter *filter)
+{
+ int i;
+ uint32 *values;
+ Size len;
+ BloomFilter *newfilter;
+
+ Assert(filter->nbits % 8 == 0);
+
+ /*
+ * The new filter is allocated with all the memory, directly into
+ * the HASH phase.
+ */
+ len = offsetof(BloomFilter, data) + (filter->nbits / 8);
+
+ newfilter = (BloomFilter *) palloc0(len);
+
+ newfilter->nhashes = filter->nhashes;
+ newfilter->nbits = filter->nbits;
+ newfilter->flags |= BLOOM_FLAG_PHASE_HASH;
+
+ SET_VARSIZE(newfilter, len);
+
+ values = (uint32 *) filter->data;
+
+ for (i = 0; i < filter->nvalues; i++)
+ /* ignore the return value here, re don't repalloc in hashing mode */
+ bloom_add_value(newfilter, values[i], NULL);
+
+ /* free the original filter, return the newly allocated one */
+ pfree(filter);
+
+ return newfilter;
+}
+
+/*
+ * bloom_contains_value
+ * Check if the bloom filter contains a particular value.
+ */
+static bool
+bloom_contains_value(BloomFilter *filter, uint32 value)
+{
+ int i;
+ uint32 big_h, h, d;
+
+ Assert(filter);
+
+ /* in sorted mode we simply search the two arrays (sorted, unsorted) */
+ if (BLOOM_IS_SORTED(filter))
+ {
+ int i;
+ uint32 *values = (uint32 *) filter->data;
+
+ /* first search through the sorted part */
+ if ((filter->nsorted > 0) &&
+ (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL))
+ return true;
+
+ /* now search through the unsorted part - linear search */
+ for (i = filter->nsorted; i < filter->nvalues; i++)
+ {
+ if (value == values[i])
+ return true;
+ }
+
+ /* nothing found */
+ return false;
+ }
+
+ /* now the regular hashing mode */
+ Assert(BLOOM_IS_HASHED(filter));
+
+ big_h = ((uint32) DatumGetInt64(hash_uint32(value)));
+
+ h = big_h % filter->nbits;
+ d = big_h % (filter->nbits - 1);
+
+ /* compute the requested number of hashes */
+ for (i = 0; i < filter->nhashes; i++)
+ {
+ int byte = (h / 8);
+ int bit = (h % 8);
+
+ /* if the bit is not set, the value is not there */
+ if (! (filter->data[byte] & (0x01 << bit)))
+ return false;
+
+ /* next bit */
+ h += d++;
+ if (h >= filter->nbits)
+ h -= filter->nbits;
+
+ if (d == filter->nbits)
+ d = 0;
+ }
+
+ /* all hashes found in bloom filter */
+ return true;
+}
+
+typedef struct BloomOpaque
+{
+ /*
+ * XXX At this point we only need a single proc (to compute the hash),
+ * but let's keep the array just like inclusion and minman opclasses,
+ * for consistency. We may need additional procs in the future.
+ */
+ FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS];
+ bool extra_proc_missing[BLOOM_MAX_PROCNUMS];
+} BloomOpaque;
+
+static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno,
+ uint16 procnum);
+
+
+Datum
+brin_bloom_opcinfo(PG_FUNCTION_ARGS)
+{
+ BrinOpcInfo *result;
+
+ /*
+ * opaque->strategy_procinfos is initialized lazily; here it is set to
+ * all-uninitialized by palloc0 which sets fn_oid to InvalidOid.
+ *
+ * bloom indexes only store the filter as a single BYTEA column
+ */
+
+ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) +
+ sizeof(BloomOpaque));
+ result->oi_nstored = 1;
+ result->oi_regular_nulls = true;
+ result->oi_opaque = (BloomOpaque *)
+ MAXALIGN((char *) result + SizeofBrinOpcInfo(1));
+ result->oi_typcache[0] = lookup_type_cache(BRINBLOOMSUMMARYOID, 0);
+
+ PG_RETURN_POINTER(result);
+}
+
+/*
+ * brin_bloom_get_ndistinct
+ * Determine the ndistinct value used to size bloom filter.
+ *
+ * Tweak the ndistinct value based on the pagesPerRange value. First,
+ * if it's negative, it's assumed to be relative to maximum number of
+ * tuples in the range (assuming each page gets MaxHeapTuplesPerPage
+ * tuples, which is likely a significant over-estimate). We also clamp
+ * the value, not to over-size the bloom filter unnecessarily.
+ *
+ * XXX We can only do this when the pagesPerRange value was supplied.
+ * If it wasn't, it has to be a read-only access to the index, in which
+ * case we don't really care. But perhaps we should fall-back to the
+ * default pagesPerRange value?
+ *
+ * XXX We might also fetch info about ndistinct estimate for the column,
+ * and compute the expected number of distinct values in a range. But
+ * that may be tricky due to data being sorted in various ways, so it
+ * seems better to rely on the upper estimate.
+ */
+static int
+brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts)
+{
+ double ndistinct;
+ double maxtuples;
+ BlockNumber pagesPerRange;
+
+ pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
+ ndistinct = BloomGetNDistinctPerRange(opts);
+
+ Assert(BlockNumberIsValid(pagesPerRange));
+
+ maxtuples = MaxHeapTuplesPerPage * pagesPerRange;
+
+ /*
+ * Similarly to n_distinct, negative values are relative - in this
+ * case to maximum number of tuples in the page range (maxtuples).
+ */
+ if (ndistinct < 0)
+ ndistinct = (-ndistinct) * maxtuples;
+
+ /*
+ * Positive values are to be used directly, but we still apply a
+ * couple of safeties no to use unreasonably small bloom filters.
+ */
+ ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE);
+
+ /*
+ * And don't use more than the maximum possible number of tuples,
+ * in the range, which would be entirely wasteful.
+ */
+ ndistinct = Min(ndistinct, maxtuples);
+
+ return (int) ndistinct;
+}
+
+static double
+brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts)
+{
+ return BloomGetFalsePositiveRate(opts);
+}
+
+/*
+ * Examine the given index tuple (which contains partial status of a certain
+ * page range) by comparing it to the given value that comes from another heap
+ * tuple. If the new value is outside the bloom filter specified by the
+ * existing tuple values, update the index tuple and return true. Otherwise,
+ * return false and do not modify in this case.
+ */
+Datum
+brin_bloom_add_value(PG_FUNCTION_ARGS)
+{
+ BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+ BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
+ Datum newval = PG_GETARG_DATUM(2);
+ bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3);
+ BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION();
+ FmgrInfo *hashFn;
+ uint32 hashValue;
+ bool updated = false;
+ AttrNumber attno;
+ BloomFilter *filter;
+
+ Assert(!isnull);
+
+ attno = column->bv_attno;
+
+ /*
+ * If this is the first non-null value, we need to initialize the bloom
+ * filter. Otherwise just extract the existing bloom filter from BrinValues.
+ */
+ if (column->bv_allnulls)
+ {
+ filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts),
+ brin_bloom_get_fp_rate(bdesc, opts));
+ column->bv_values[0] = PointerGetDatum(filter);
+ column->bv_allnulls = false;
+ updated = true;
+ }
+ else
+ filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]);
+
+ /*
+ * Compute the hash of the new value, using the supplied hash function,
+ * and then add the hash value to the bloom filter.
+ */
+ hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH);
+
+ hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval));
+
+ filter = bloom_add_value(filter, hashValue, &updated);
+
+ column->bv_values[0] = PointerGetDatum(filter);
+
+ PG_RETURN_BOOL(updated);
+}
+
+/*
+ * 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 bloom
+ * filter. Return true if so, false otherwise.
+ */
+Datum
+brin_bloom_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();
+ AttrNumber attno;
+ Datum value;
+ Datum matches;
+ FmgrInfo *finfo;
+ uint32 hashValue;
+ BloomFilter *filter;
+ int keyno;
+
+ filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]);
+
+ Assert(filter);
+
+ matches = true;
+
+ 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));
+
+ attno = key->sk_attno;
+ value = key->sk_argument;
+
+ switch (key->sk_strategy)
+ {
+ case BloomEqualStrategyNumber:
+
+ /*
+ * In the equality case (WHERE col = someval), we want to return
+ * the current page range if the minimum value in the range <=
+ * scan key, and the maximum value >= scan key.
+ */
+ finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH);
+
+ hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value));
+ matches &= bloom_contains_value(filter, hashValue);
+
+ break;
+ default:
+ /* shouldn't happen */
+ elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+ matches = 0;
+ break;
+ }
+
+ if (!matches)
+ break;
+ }
+
+ PG_RETURN_DATUM(matches);
+}
+
+/*
+ * Given two BrinValues, update the first of them as a union of the summary
+ * values contained in both. The second one is untouched.
+ *
+ * XXX We assume the bloom filters have the same parameters fow now. In the
+ * future we should have 'can union' function, to decide if we can combine
+ * two particular bloom filters.
+ */
+Datum
+brin_bloom_union(PG_FUNCTION_ARGS)
+{
+ BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
+ BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
+ BloomFilter *filter_a;
+ BloomFilter *filter_b;
+
+ Assert(col_a->bv_attno == col_b->bv_attno);
+ Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
+
+ filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]);
+ filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]);
+
+ /* make sure neither of the bloom filters is NULL */
+ Assert(filter_a && filter_b);
+ Assert(filter_a->nbits == filter_b->nbits);
+
+ /*
+ * Merging of the filters depends on the phase of both filters.
+ */
+ if (BLOOM_IS_SORTED(filter_b))
+ {
+ /*
+ * Simply read all items from 'b' and add them to 'a' (the phase of
+ * 'a' does not really matter).
+ */
+ int i;
+ uint32 *values = (uint32 *) filter_b->data;
+
+ for (i = 0; i < filter_b->nvalues; i++)
+ filter_a = bloom_add_value(filter_a, values[i], NULL);
+
+ col_a->bv_values[0] = PointerGetDatum(filter_a);
+ }
+ else if (BLOOM_IS_SORTED(filter_a))
+ {
+ /*
+ * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values
+ * from 'a' into the new copy.
+ */
+ int i;
+ BloomFilter *filter_c;
+ uint32 *values = (uint32 *) filter_a->data;
+
+ filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1));
+
+ for (i = 0; i < filter_a->nvalues; i++)
+ filter_c = bloom_add_value(filter_c, values[i], NULL);
+
+ col_a->bv_values[0] = PointerGetDatum(filter_c);
+ }
+ else if (BLOOM_IS_HASHED(filter_a))
+ {
+ /*
+ * 'b' hashed, 'a' hashed - merge the bitmaps by OR
+ */
+ int i;
+ int nbytes = (filter_a->nbits + 7) / 8;
+
+ /* we better have "compatible" bloom filters */
+ Assert(filter_a->nbits == filter_b->nbits);
+ Assert(filter_a->nhashes == filter_b->nhashes);
+
+ for (i = 0; i < nbytes; i++)
+ filter_a->data[i] |= filter_b->data[i];
+ }
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Cache and return inclusion opclass support procedure
+ *
+ * Return the procedure corresponding to the given function support number
+ * or null if it is not exists.
+ */
+static FmgrInfo *
+bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
+{
+ BloomOpaque *opaque;
+ uint16 basenum = procnum - PROCNUM_BASE;
+
+ /*
+ * We cache these in the opaque struct, to avoid repetitive syscache
+ * lookups.
+ */
+ opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+ /*
+ * If we already searched for this proc and didn't find it, don't bother
+ * searching again.
+ */
+ if (opaque->extra_proc_missing[basenum])
+ return NULL;
+
+ if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
+ {
+ if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
+ procnum)))
+ {
+ fmgr_info_copy(&opaque->extra_procinfos[basenum],
+ index_getprocinfo(bdesc->bd_index, attno, procnum),
+ bdesc->bd_context);
+ }
+ else
+ {
+ opaque->extra_proc_missing[basenum] = true;
+ return NULL;
+ }
+ }
+
+ return &opaque->extra_procinfos[basenum];
+}
+
+Datum
+brin_bloom_options(PG_FUNCTION_ARGS)
+{
+ local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+ init_local_reloptions(relopts, sizeof(BloomOptions));
+
+ add_local_real_reloption(relopts, "n_distinct_per_range",
+ "number of distinct items expected in a BRIN page range",
+ BLOOM_DEFAULT_NDISTINCT_PER_RANGE,
+ -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange));
+
+ add_local_real_reloption(relopts, "false_positive_rate",
+ "desired false-positive rate for the bloom filters",
+ BLOOM_DEFAULT_FALSE_POSITIVE_RATE,
+ 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate));
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * brin_bloom_summary_in
+ * - input routine for type brin_bloom_summary.
+ *
+ * brin_bloom_summary is only used internally to represent summaries
+ * in BRIN bloom indexes, so it has no operations of its own, and we
+ * disallow input too.
+ */
+Datum
+brin_bloom_summary_in(PG_FUNCTION_ARGS)
+{
+ /*
+ * brin_bloom_summary stores the data in binary form and parsing
+ * text input is not needed, so disallow this.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+
+/*
+ * brin_bloom_summary_out
+ * - output routine for type brin_bloom_summary.
+ *
+ * BRIN bloom summaries are serialized into a bytea value, but we want
+ * to output something nicer humans can understand.
+ */
+Datum
+brin_bloom_summary_out(PG_FUNCTION_ARGS)
+{
+ BloomFilter *filter;
+ StringInfoData str;
+
+ initStringInfo(&str);
+ appendStringInfoChar(&str, '{');
+
+ /*
+ * XXX not sure the detoasting is necessary (probably not, this
+ * can only be in an index).
+ */
+ filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+
+ if (BLOOM_IS_HASHED(filter))
+ {
+ appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u",
+ filter->nhashes, filter->nbits, filter->nbits_set);
+ }
+ else
+ {
+ appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u",
+ filter->nvalues, filter->nsorted);
+ /* TODO include the sorted/unsorted values */
+ }
+
+ appendStringInfoChar(&str, '}');
+
+ PG_RETURN_CSTRING(str.data);
+}
+
+/*
+ * brin_bloom_summary_recv
+ * - binary input routine for type brin_bloom_summary.
+ */
+Datum
+brin_bloom_summary_recv(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * brin_bloom_summary_send
+ * - binary output routine for type brin_bloom_summary.
+ *
+ * BRIN bloom summaries are serialized in a bytea value (although the
+ * type is named differently), so let's just send that.
+ */
+Datum
+brin_bloom_summary_send(PG_FUNCTION_ARGS)
+{
+ return byteasend(fcinfo);
+}
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 0987878dd2..b02298c0aa 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -22,6 +22,8 @@ typedef struct BrinOptions
int32 vl_len_; /* varlena header (do not touch directly!) */
BlockNumber pagesPerRange;
bool autosummarize;
+ double nDistinctPerRange; /* number of distinct values per range */
+ double falsePositiveRate; /* false positive for bloom filter */
} BrinOptions;
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 7c4f3da0a0..e3c9d47503 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -58,6 +58,10 @@ typedef struct BrinDesc
/* total number of Datum entries that are stored on-disk for all columns */
int bd_totalstored;
+ /* parameters for sizing bloom filter (BRIN bloom opclasses) */
+ double bd_nDistinctPerRange;
+ double bd_falsePositiveRange;
+
/* per-column info; bd_tupdesc->natts entries long */
BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER];
} BrinDesc;
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 1dfb6fd373..12033d48ec 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1705,6 +1705,11 @@
amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)',
amopmethod => 'brin' },
+# bloom bytea
+{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea',
+ amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)',
+ amopmethod => 'brin' },
+
# minmax "char"
{ amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char',
amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)',
@@ -1722,6 +1727,11 @@
amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)',
amopmethod => 'brin' },
+# bloom "char"
+{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char',
+ amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)',
+ amopmethod => 'brin' },
+
# minmax name
{ amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name',
amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)',
@@ -1739,6 +1749,11 @@
amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)',
amopmethod => 'brin' },
+# bloom name
+{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name',
+ amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)',
+ amopmethod => 'brin' },
+
# minmax integer
{ amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8',
@@ -1885,6 +1900,44 @@
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
amopmethod => 'brin' },
+# bloom integer
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)',
+ amopmethod => 'brin' },
+
# minmax text
{ amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text',
amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)',
@@ -1902,6 +1955,11 @@
amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)',
amopmethod => 'brin' },
+# bloom text
+{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text',
+ amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)',
+ amopmethod => 'brin' },
+
# minmax oid
{ amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid',
amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)',
@@ -1919,6 +1977,11 @@
amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)',
amopmethod => 'brin' },
+# bloom oid
+{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)',
+ amopmethod => 'brin' },
+
# minmax tid
{ amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid',
amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)',
@@ -1936,6 +1999,11 @@
amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)',
amopmethod => 'brin' },
+# tid oid
+{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)',
+ amopmethod => 'brin' },
+
# minmax float (float4, float8)
{ amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4',
@@ -2002,6 +2070,20 @@
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
amopmethod => 'brin' },
+# bloom float
+{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)',
+ amopmethod => 'brin' },
+
# minmax macaddr
{ amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr',
amoprighttype => 'macaddr', amopstrategy => '1',
@@ -2019,6 +2101,11 @@
amoprighttype => 'macaddr', amopstrategy => '5',
amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' },
+# bloom macaddr
+{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '1',
+ amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' },
+
# minmax macaddr8
{ amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8',
amoprighttype => 'macaddr8', amopstrategy => '1',
@@ -2036,6 +2123,11 @@
amoprighttype => 'macaddr8', amopstrategy => '5',
amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' },
+# bloom macaddr8
+{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '1',
+ amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' },
+
# minmax inet
{ amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet',
amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)',
@@ -2053,6 +2145,11 @@
amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)',
amopmethod => 'brin' },
+# bloom inet
+{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)',
+ amopmethod => 'brin' },
+
# inclusion inet
{ amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet',
amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)',
@@ -2090,6 +2187,11 @@
amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)',
amopmethod => 'brin' },
+# bloom character
+{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar',
+ amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)',
+ amopmethod => 'brin' },
+
# minmax time without time zone
{ amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time',
amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)',
@@ -2107,6 +2209,11 @@
amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
amopmethod => 'brin' },
+# bloom time without time zone
+{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)',
+ amopmethod => 'brin' },
+
# minmax datetime (date, timestamp, timestamptz)
{ amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp',
@@ -2253,6 +2360,44 @@
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' },
+# bloom datetime (date, timestamp, timestamptz)
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '=(date,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '=(date,timestamptz)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '1',
+ amopopr => '=(timestamptz,date)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' },
+
# minmax interval
{ amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval',
amoprighttype => 'interval', amopstrategy => '1',
@@ -2270,6 +2415,11 @@
amoprighttype => 'interval', amopstrategy => '5',
amopopr => '>(interval,interval)', amopmethod => 'brin' },
+# bloom interval
+{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '1',
+ amopopr => '=(interval,interval)', amopmethod => 'brin' },
+
# minmax time with time zone
{ amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz',
amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)',
@@ -2287,6 +2437,11 @@
amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)',
amopmethod => 'brin' },
+# bloom time with time zone
+{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)',
+ amopmethod => 'brin' },
+
# minmax bit
{ amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit',
amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)',
@@ -2338,6 +2493,11 @@
amoprighttype => 'numeric', amopstrategy => '5',
amopopr => '>(numeric,numeric)', amopmethod => 'brin' },
+# bloom numeric
+{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '1',
+ amopopr => '=(numeric,numeric)', amopmethod => 'brin' },
+
# minmax uuid
{ amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid',
amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)',
@@ -2355,6 +2515,11 @@
amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)',
amopmethod => 'brin' },
+# bloom uuid
+{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)',
+ amopmethod => 'brin' },
+
# inclusion range types
{ amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange',
amoprighttype => 'anyrange', amopstrategy => '1',
@@ -2416,6 +2581,11 @@
amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)',
amopmethod => 'brin' },
+# bloom pg_lsn
+{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)',
+ amopmethod => 'brin' },
+
# inclusion box
{ amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box',
amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 37b580883f..4e35bb3459 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -770,6 +770,24 @@
{ amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea',
amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom bytea
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' },
+
# minmax "char"
{ amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char',
amprocrighttype => 'char', amprocnum => '1',
@@ -783,6 +801,24 @@
{ amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char',
amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom "char"
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' },
+
# minmax name
{ amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name',
amprocrighttype => 'name', amprocnum => '1',
@@ -796,6 +832,24 @@
{ amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name',
amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom name
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' },
+
# minmax integer: int2, int4, int8
{ amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8',
amprocrighttype => 'int8', amprocnum => '1',
@@ -897,6 +951,58 @@
{ amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4',
amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom integer: int2, int4, int8
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' },
+
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' },
+
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' },
+
# minmax text
{ amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text',
amprocrighttype => 'text', amprocnum => '1',
@@ -910,6 +1016,24 @@
{ amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text',
amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom text
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' },
+
# minmax oid
{ amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid',
amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' },
@@ -922,6 +1046,23 @@
{ amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid',
amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom oid
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' },
+
# minmax tid
{ amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid',
amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' },
@@ -934,6 +1075,23 @@
{ amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid',
amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom tid
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' },
+
# minmax float
{ amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4',
amprocrighttype => 'float4', amprocnum => '1',
@@ -984,6 +1142,45 @@
amprocrighttype => 'float4', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom float
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '11',
+ amproc => 'hashfloat4' },
+
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '11',
+ amproc => 'hashfloat8' },
+
# minmax macaddr
{ amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr',
amprocrighttype => 'macaddr', amprocnum => '1',
@@ -998,6 +1195,26 @@
amprocrighttype => 'macaddr', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom macaddr
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '11',
+ amproc => 'hashmacaddr' },
+
# minmax macaddr8
{ amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8',
amprocrighttype => 'macaddr8', amprocnum => '1',
@@ -1012,6 +1229,26 @@
amprocrighttype => 'macaddr8', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom macaddr8
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '11',
+ amproc => 'hashmacaddr8' },
+
# minmax inet
{ amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '1',
@@ -1025,6 +1262,24 @@
{ amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom inet
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' },
+
# inclusion inet
{ amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '1',
@@ -1059,6 +1314,26 @@
amprocrighttype => 'bpchar', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom character
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '11',
+ amproc => 'hashchar' },
+
# minmax time without time zone
{ amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time',
amprocrighttype => 'time', amprocnum => '1',
@@ -1072,6 +1347,24 @@
{ amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time',
amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom time without time zone
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' },
+
# minmax datetime (date, timestamp, timestamptz)
{ amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp',
amprocrighttype => 'timestamp', amprocnum => '1',
@@ -1179,6 +1472,62 @@
amprocrighttype => 'timestamptz', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom datetime (date, timestamp, timestamptz)
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '11',
+ amproc => 'timestamp_hash' },
+
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '11',
+ amproc => 'timestamp_hash' },
+
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' },
+
# minmax interval
{ amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval',
amprocrighttype => 'interval', amprocnum => '1',
@@ -1193,6 +1542,26 @@
amprocrighttype => 'interval', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom interval
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '11',
+ amproc => 'interval_hash' },
+
# minmax time with time zone
{ amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz',
amprocrighttype => 'timetz', amprocnum => '1',
@@ -1207,6 +1576,26 @@
amprocrighttype => 'timetz', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom time with time zone
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '11',
+ amproc => 'timetz_hash' },
+
# minmax bit
{ amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit',
amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' },
@@ -1247,6 +1636,26 @@
amprocrighttype => 'numeric', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom numeric
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '11',
+ amproc => 'hash_numeric' },
+
# minmax uuid
{ amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid',
amprocrighttype => 'uuid', amprocnum => '1',
@@ -1260,6 +1669,24 @@
{ amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid',
amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom uuid
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' },
+
# inclusion range types
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1',
@@ -1295,6 +1722,26 @@
amprocrighttype => 'pg_lsn', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom pg_lsn
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '11',
+ amproc => 'pg_lsn_hash' },
+
# inclusion box
{ amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box',
amprocrighttype => 'box', amprocnum => '1',
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index f2342bb328..ca747a03b9 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -257,67 +257,130 @@
{ opcmethod => 'brin', opcname => 'bytea_minmax_ops',
opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea',
opckeytype => 'bytea' },
+{ opcmethod => 'brin', opcname => 'bytea_bloom_ops',
+ opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea',
+ opckeytype => 'bytea', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'char_minmax_ops',
opcfamily => 'brin/char_minmax_ops', opcintype => 'char',
opckeytype => 'char' },
+{ opcmethod => 'brin', opcname => 'char_bloom_ops',
+ opcfamily => 'brin/char_bloom_ops', opcintype => 'char',
+ opckeytype => 'char', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'name_minmax_ops',
opcfamily => 'brin/name_minmax_ops', opcintype => 'name',
opckeytype => 'name' },
+{ opcmethod => 'brin', opcname => 'name_bloom_ops',
+ opcfamily => 'brin/name_bloom_ops', opcintype => 'name',
+ opckeytype => 'name', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int8_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8',
opckeytype => 'int8' },
+{ opcmethod => 'brin', opcname => 'int8_bloom_ops',
+ opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8',
+ opckeytype => 'int8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int2_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2',
opckeytype => 'int2' },
+{ opcmethod => 'brin', opcname => 'int2_bloom_ops',
+ opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2',
+ opckeytype => 'int2', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int4_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4',
opckeytype => 'int4' },
+{ opcmethod => 'brin', opcname => 'int4_bloom_ops',
+ opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4',
+ opckeytype => 'int4', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'text_minmax_ops',
opcfamily => 'brin/text_minmax_ops', opcintype => 'text',
opckeytype => 'text' },
+{ opcmethod => 'brin', opcname => 'text_bloom_ops',
+ opcfamily => 'brin/text_bloom_ops', opcintype => 'text',
+ opckeytype => 'text', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'oid_minmax_ops',
opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' },
+{ opcmethod => 'brin', opcname => 'oid_bloom_ops',
+ opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid',
+ opckeytype => 'oid', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'tid_minmax_ops',
opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' },
+{ opcmethod => 'brin', opcname => 'tid_bloom_ops',
+ opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid',
+ opcdefault => 'f'},
{ opcmethod => 'brin', opcname => 'float4_minmax_ops',
opcfamily => 'brin/float_minmax_ops', opcintype => 'float4',
opckeytype => 'float4' },
+{ opcmethod => 'brin', opcname => 'float4_bloom_ops',
+ opcfamily => 'brin/float_bloom_ops', opcintype => 'float4',
+ opckeytype => 'float4', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'float8_minmax_ops',
opcfamily => 'brin/float_minmax_ops', opcintype => 'float8',
opckeytype => 'float8' },
+{ opcmethod => 'brin', opcname => 'float8_bloom_ops',
+ opcfamily => 'brin/float_bloom_ops', opcintype => 'float8',
+ opckeytype => 'float8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr_minmax_ops',
opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr',
opckeytype => 'macaddr' },
+{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops',
+ opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr',
+ opckeytype => 'macaddr', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr8_minmax_ops',
opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8',
opckeytype => 'macaddr8' },
+{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops',
+ opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8',
+ opckeytype => 'macaddr8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'inet_minmax_ops',
opcfamily => 'brin/network_minmax_ops', opcintype => 'inet',
opcdefault => 'f', opckeytype => 'inet' },
+{ opcmethod => 'brin', opcname => 'inet_bloom_ops',
+ opcfamily => 'brin/network_bloom_ops', opcintype => 'inet',
+ opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'inet_inclusion_ops',
opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet',
opckeytype => 'inet' },
{ opcmethod => 'brin', opcname => 'bpchar_minmax_ops',
opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar',
opckeytype => 'bpchar' },
+{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops',
+ opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar',
+ opckeytype => 'bpchar', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'time_minmax_ops',
opcfamily => 'brin/time_minmax_ops', opcintype => 'time',
opckeytype => 'time' },
+{ opcmethod => 'brin', opcname => 'time_bloom_ops',
+ opcfamily => 'brin/time_bloom_ops', opcintype => 'time',
+ opckeytype => 'time', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'date_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date',
opckeytype => 'date' },
+{ opcmethod => 'brin', opcname => 'date_bloom_ops',
+ opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date',
+ opckeytype => 'date', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamp_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp',
opckeytype => 'timestamp' },
+{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops',
+ opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp',
+ opckeytype => 'timestamp', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamptz_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz',
opckeytype => 'timestamptz' },
+{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops',
+ opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz',
+ opckeytype => 'timestamptz', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'interval_minmax_ops',
opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval',
opckeytype => 'interval' },
+{ opcmethod => 'brin', opcname => 'interval_bloom_ops',
+ opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval',
+ opckeytype => 'interval', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timetz_minmax_ops',
opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz',
opckeytype => 'timetz' },
+{ opcmethod => 'brin', opcname => 'timetz_bloom_ops',
+ opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz',
+ opckeytype => 'timetz', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'bit_minmax_ops',
opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' },
{ opcmethod => 'brin', opcname => 'varbit_minmax_ops',
@@ -326,18 +389,27 @@
{ opcmethod => 'brin', opcname => 'numeric_minmax_ops',
opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric',
opckeytype => 'numeric' },
+{ opcmethod => 'brin', opcname => 'numeric_bloom_ops',
+ opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric',
+ opckeytype => 'numeric', opcdefault => 'f' },
# no brin opclass for record, anyarray
{ opcmethod => 'brin', opcname => 'uuid_minmax_ops',
opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid',
opckeytype => 'uuid' },
+{ opcmethod => 'brin', opcname => 'uuid_bloom_ops',
+ opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid',
+ opckeytype => 'uuid', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'range_inclusion_ops',
opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange',
opckeytype => 'anyrange' },
{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops',
opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn',
opckeytype => 'pg_lsn' },
+{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops',
+ opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn',
+ opckeytype => 'pg_lsn', opcdefault => 'f' },
# no brin opclass for enum, tsvector, tsquery, jsonb
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index cf0fb325b3..8875079698 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -180,50 +180,88 @@
opfmethod => 'gin', opfname => 'jsonb_path_ops' },
{ oid => '4054',
opfmethod => 'brin', opfname => 'integer_minmax_ops' },
+{ oid => '8100',
+ opfmethod => 'brin', opfname => 'integer_bloom_ops' },
{ oid => '4055',
opfmethod => 'brin', opfname => 'numeric_minmax_ops' },
{ oid => '4056',
opfmethod => 'brin', opfname => 'text_minmax_ops' },
+{ oid => '8101',
+ opfmethod => 'brin', opfname => 'text_bloom_ops' },
+{ oid => '8102',
+ opfmethod => 'brin', opfname => 'numeric_bloom_ops' },
{ oid => '4058',
opfmethod => 'brin', opfname => 'timetz_minmax_ops' },
+{ oid => '8103',
+ opfmethod => 'brin', opfname => 'timetz_bloom_ops' },
{ oid => '4059',
opfmethod => 'brin', opfname => 'datetime_minmax_ops' },
+{ oid => '8104',
+ opfmethod => 'brin', opfname => 'datetime_bloom_ops' },
{ oid => '4062',
opfmethod => 'brin', opfname => 'char_minmax_ops' },
+{ oid => '8105',
+ opfmethod => 'brin', opfname => 'char_bloom_ops' },
{ oid => '4064',
opfmethod => 'brin', opfname => 'bytea_minmax_ops' },
+{ oid => '8106',
+ opfmethod => 'brin', opfname => 'bytea_bloom_ops' },
{ oid => '4065',
opfmethod => 'brin', opfname => 'name_minmax_ops' },
+{ oid => '8107',
+ opfmethod => 'brin', opfname => 'name_bloom_ops' },
{ oid => '4068',
opfmethod => 'brin', opfname => 'oid_minmax_ops' },
+{ oid => '8108',
+ opfmethod => 'brin', opfname => 'oid_bloom_ops' },
{ oid => '4069',
opfmethod => 'brin', opfname => 'tid_minmax_ops' },
+{ oid => '8123',
+ opfmethod => 'brin', opfname => 'tid_bloom_ops' },
{ oid => '4070',
opfmethod => 'brin', opfname => 'float_minmax_ops' },
+{ oid => '8109',
+ opfmethod => 'brin', opfname => 'float_bloom_ops' },
{ oid => '4074',
opfmethod => 'brin', opfname => 'macaddr_minmax_ops' },
+{ oid => '8110',
+ opfmethod => 'brin', opfname => 'macaddr_bloom_ops' },
{ oid => '4109',
opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' },
+{ oid => '8111',
+ opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' },
{ oid => '4075',
opfmethod => 'brin', opfname => 'network_minmax_ops' },
{ oid => '4102',
opfmethod => 'brin', opfname => 'network_inclusion_ops' },
+{ oid => '8112',
+ opfmethod => 'brin', opfname => 'network_bloom_ops' },
{ oid => '4076',
opfmethod => 'brin', opfname => 'bpchar_minmax_ops' },
+{ oid => '8113',
+ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' },
{ oid => '4077',
opfmethod => 'brin', opfname => 'time_minmax_ops' },
+{ oid => '8114',
+ opfmethod => 'brin', opfname => 'time_bloom_ops' },
{ oid => '4078',
opfmethod => 'brin', opfname => 'interval_minmax_ops' },
+{ oid => '8115',
+ opfmethod => 'brin', opfname => 'interval_bloom_ops' },
{ oid => '4079',
opfmethod => 'brin', opfname => 'bit_minmax_ops' },
{ oid => '4080',
opfmethod => 'brin', opfname => 'varbit_minmax_ops' },
{ oid => '4081',
opfmethod => 'brin', opfname => 'uuid_minmax_ops' },
+{ oid => '8116',
+ opfmethod => 'brin', opfname => 'uuid_bloom_ops' },
{ oid => '4103',
opfmethod => 'brin', opfname => 'range_inclusion_ops' },
{ oid => '4082',
opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' },
+{ oid => '8117',
+ opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' },
{ oid => '4104',
opfmethod => 'brin', opfname => 'box_inclusion_ops' },
{ oid => '5000',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 925b262b60..dc17d4ce38 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8127,6 +8127,26 @@
proargtypes => 'internal internal internal',
prosrc => 'brin_inclusion_union' },
+# BRIN bloom
+{ oid => '8118', descr => 'BRIN bloom support',
+ proname => 'brin_bloom_opcinfo', prorettype => 'internal',
+ proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' },
+{ oid => '8119', descr => 'BRIN bloom support',
+ proname => 'brin_bloom_add_value', prorettype => 'bool',
+ proargtypes => 'internal internal internal internal',
+ prosrc => 'brin_bloom_add_value' },
+{ oid => '8120', descr => 'BRIN bloom support',
+ proname => 'brin_bloom_consistent', prorettype => 'bool',
+ proargtypes => 'internal internal internal int4',
+ prosrc => 'brin_bloom_consistent' },
+{ oid => '8121', descr => 'BRIN bloom support',
+ proname => 'brin_bloom_union', prorettype => 'bool',
+ proargtypes => 'internal internal internal',
+ prosrc => 'brin_bloom_union' },
+{ oid => '8122', descr => 'BRIN bloom support',
+ proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f',
+ proargtypes => 'internal', prosrc => 'brin_bloom_options' },
+
# userlock replacements
{ oid => '2880', descr => 'obtain exclusive advisory lock',
proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r',
@@ -10973,3 +10993,17 @@
prosrc => 'unicode_is_normalized' },
]
+
+{ oid => '9035', descr => 'I/O',
+ proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary',
+ proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' },
+{ oid => '9036', descr => 'I/O',
+ proname => 'brin_bloom_summary_out', prorettype => 'cstring',
+ proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' },
+{ oid => '9037', descr => 'I/O',
+ proname => 'brin_bloom_summary_recv', provolatile => 's',
+ prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal',
+ prosrc => 'brin_bloom_summary_recv' },
+{ oid => '9038', descr => 'I/O',
+ proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index b2cec07416..a41c2e5418 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -631,3 +631,10 @@
typalign => 'd', typstorage => 'x' },
]
+
+{ oid => '9034', oid_symbol => 'BRINBLOOMSUMMARYOID',
+ descr => 'BRIN bloom summary',
+ typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S',
+ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out',
+ typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send',
+ typalign => 'i', typstorage => 'x', typcollation => 'default' },
diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out
new file mode 100644
index 0000000000..d58a4a483c
--- /dev/null
+++ b/src/test/regress/expected/brin_bloom.out
@@ -0,0 +1,456 @@
+CREATE TABLE brintest_bloom (byteacol bytea,
+ charcol "char",
+ namecol name,
+ int8col bigint,
+ int2col smallint,
+ int4col integer,
+ textcol text,
+ oidcol oid,
+ float4col real,
+ float8col double precision,
+ macaddrcol macaddr,
+ inetcol inet,
+ cidrcol cidr,
+ bpcharcol character,
+ datecol date,
+ timecol time without time zone,
+ timestampcol timestamp without time zone,
+ timestamptzcol timestamp with time zone,
+ intervalcol interval,
+ timetzcol time with time zone,
+ numericcol numeric,
+ uuidcol uuid,
+ lsncol pg_lsn
+) WITH (fillfactor=10);
+INSERT INTO brintest_bloom SELECT
+ repeat(stringu1, 8)::bytea,
+ substr(stringu1, 1, 1)::"char",
+ stringu1::name, 142857 * tenthous,
+ thousand,
+ twothousand,
+ repeat(stringu1, 8),
+ unique1::oid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4/24' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ substr(stringu1, 1, 1)::bpchar,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20+02' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 100;
+-- throw in some NULL's and different values
+INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT
+ inet 'fe80::6e40:8ff:fea9:8c46' + tenthous,
+ cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous
+FROM tenk1 ORDER BY thousand, tenthous LIMIT 25;
+-- test bloom specific index options
+-- ndistinct must be >= -1.0
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(n_distinct_per_range = -1.1)
+);
+ERROR: value -1.1 out of bounds for option "n_distinct_per_range"
+DETAIL: Valid values are between "-1.000000" and "2147483647.000000".
+-- false_positive_rate must be between 0.001 and 1.0
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(false_positive_rate = 0.0)
+);
+ERROR: value 0.0 out of bounds for option "false_positive_rate"
+DETAIL: Valid values are between "0.001000" and "1.000000".
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(false_positive_rate = 1.01)
+);
+ERROR: value 1.01 out of bounds for option "false_positive_rate"
+DETAIL: Valid values are between "0.001000" and "1.000000".
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops,
+ charcol char_bloom_ops,
+ namecol name_bloom_ops,
+ int8col int8_bloom_ops,
+ int2col int2_bloom_ops,
+ int4col int4_bloom_ops,
+ textcol text_bloom_ops,
+ oidcol oid_bloom_ops,
+ float4col float4_bloom_ops,
+ float8col float8_bloom_ops,
+ macaddrcol macaddr_bloom_ops,
+ inetcol inet_bloom_ops,
+ cidrcol inet_bloom_ops,
+ bpcharcol bpchar_bloom_ops,
+ datecol date_bloom_ops,
+ timecol time_bloom_ops,
+ timestampcol timestamp_bloom_ops,
+ timestamptzcol timestamptz_bloom_ops,
+ intervalcol interval_bloom_ops,
+ timetzcol timetz_bloom_ops,
+ numericcol numeric_bloom_ops,
+ uuidcol uuid_bloom_ops,
+ lsncol pg_lsn_bloom_ops
+) with (pages_per_range = 1);
+CREATE TABLE brinopers_bloom (colname name, typ text,
+ op text[], value text[], matches int[],
+ check (cardinality(op) = cardinality(value)),
+ check (cardinality(op) = cardinality(matches)));
+INSERT INTO brinopers_bloom VALUES
+ ('byteacol', 'bytea',
+ '{=}',
+ '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}',
+ '{1}'),
+ ('charcol', '"char"',
+ '{=}',
+ '{M}',
+ '{6}'),
+ ('namecol', 'name',
+ '{=}',
+ '{MAAAAA}',
+ '{2}'),
+ ('int2col', 'int2',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int2col', 'int4',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int2col', 'int8',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int2',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int4',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int8',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int8col', 'int8',
+ '{=}',
+ '{1257141600}',
+ '{1}'),
+ ('textcol', 'text',
+ '{=}',
+ '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}',
+ '{1}'),
+ ('oidcol', 'oid',
+ '{=}',
+ '{8800}',
+ '{1}'),
+ ('float4col', 'float4',
+ '{=}',
+ '{1}',
+ '{4}'),
+ ('float4col', 'float8',
+ '{=}',
+ '{1}',
+ '{4}'),
+ ('float8col', 'float4',
+ '{=}',
+ '{0}',
+ '{1}'),
+ ('float8col', 'float8',
+ '{=}',
+ '{0}',
+ '{1}'),
+ ('macaddrcol', 'macaddr',
+ '{=}',
+ '{2c:00:2d:00:16:00}',
+ '{2}'),
+ ('inetcol', 'inet',
+ '{=}',
+ '{10.2.14.231/24}',
+ '{1}'),
+ ('inetcol', 'cidr',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('cidrcol', 'inet',
+ '{=}',
+ '{10.2.14/24}',
+ '{2}'),
+ ('cidrcol', 'inet',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('cidrcol', 'cidr',
+ '{=}',
+ '{10.2.14/24}',
+ '{2}'),
+ ('cidrcol', 'cidr',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('bpcharcol', 'bpchar',
+ '{=}',
+ '{W}',
+ '{6}'),
+ ('datecol', 'date',
+ '{=}',
+ '{2009-12-01}',
+ '{1}'),
+ ('timecol', 'time',
+ '{=}',
+ '{02:28:57}',
+ '{1}'),
+ ('timestampcol', 'timestamp',
+ '{=}',
+ '{1964-03-24 19:26:45}',
+ '{1}'),
+ ('timestampcol', 'timestamptz',
+ '{=}',
+ '{1964-03-24 19:26:45}',
+ '{1}'),
+ ('timestamptzcol', 'timestamptz',
+ '{=}',
+ '{1972-10-19 09:00:00-07}',
+ '{1}'),
+ ('intervalcol', 'interval',
+ '{=}',
+ '{1 mons 13 days 12:24}',
+ '{1}'),
+ ('timetzcol', 'timetz',
+ '{=}',
+ '{01:35:50+02}',
+ '{2}'),
+ ('numericcol', 'numeric',
+ '{=}',
+ '{2268164.347826086956521739130434782609}',
+ '{1}'),
+ ('uuidcol', 'uuid',
+ '{=}',
+ '{52225222-5222-5222-5222-522252225222}',
+ '{1}'),
+ ('lsncol', 'pg_lsn',
+ '{=, IS, IS NOT}',
+ '{44/455222, NULL, NULL}',
+ '{1, 25, 100}');
+DO $x$
+DECLARE
+ r record;
+ r2 record;
+ cond text;
+ idx_ctids tid[];
+ ss_ctids tid[];
+ count int;
+ plan_ok bool;
+ plan_line text;
+BEGIN
+ FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP
+
+ -- prepare the condition
+ IF r.value IS NULL THEN
+ cond := format('%I %s %L', r.colname, r.oper, r.value);
+ ELSE
+ cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ);
+ END IF;
+
+ -- run the query using the brin index
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get bitmap indexscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond)
+ INTO idx_ctids;
+
+ -- run the query using a seqscan
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get seqscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond)
+ INTO ss_ctids;
+
+ -- make sure both return the same results
+ count := array_length(idx_ctids, 1);
+
+ IF NOT (count = array_length(ss_ctids, 1) AND
+ idx_ctids @> ss_ctids AND
+ idx_ctids <@ ss_ctids) THEN
+ -- report the results of each scan to make the differences obvious
+ RAISE WARNING 'something not right in %: count %', r, count;
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP
+ RAISE NOTICE 'seqscan: %', r2;
+ END LOOP;
+
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP
+ RAISE NOTICE 'bitmapscan: %', r2;
+ END LOOP;
+ END IF;
+
+ -- make sure we found expected number of matches
+ IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF;
+ END LOOP;
+END;
+$x$;
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+INSERT INTO brintest_bloom SELECT
+ repeat(stringu1, 42)::bytea,
+ substr(stringu1, 1, 1)::"char",
+ stringu1::name, 142857 * tenthous,
+ thousand,
+ twothousand,
+ repeat(stringu1, 42),
+ unique1::oid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ substr(stringu1, 1, 1)::bpchar,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5;
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+VACUUM brintest_bloom; -- force a summarization cycle in brinidx
+UPDATE brintest_bloom SET int8col = int8col * int4col;
+UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL;
+-- Tests for brin_summarize_new_values
+SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index
+ERROR: "brintest_bloom" is not an index
+SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
+ERROR: "tenk1_unique1" is not a BRIN index
+SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected
+ brin_summarize_new_values
+---------------------------
+ 0
+(1 row)
+
+-- Tests for brin_desummarize_range
+SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range
+ERROR: block number out of range: -1
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+SELECT brin_desummarize_range('brinidx_bloom', 100000000);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+-- Test brin_summarize_range
+CREATE TABLE brin_summarize_bloom (
+ value int
+) WITH (fillfactor=10, autovacuum_enabled=false);
+CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2);
+-- Fill a few pages
+DO $$
+DECLARE curtid tid;
+BEGIN
+ LOOP
+ INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid;
+ EXIT WHEN curtid > tid '(2, 0)';
+ END LOOP;
+END;
+$$;
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 0);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- nothing: already summarized
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 1);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 2);
+ brin_summarize_range
+----------------------
+ 1
+(1 row)
+
+-- nothing: page doesn't exist in table
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- invalid block number values
+SELECT brin_summarize_range('brin_summarize_bloom_idx', -1);
+ERROR: block number out of range: -1
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296);
+ERROR: block number out of range: 4294967296
+-- test brin cost estimates behave sanely based on correlation of values
+CREATE TABLE brin_test_bloom (a INT, b INT);
+INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x);
+CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2);
+CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2);
+VACUUM ANALYZE brin_test_bloom;
+-- Ensure brin index is used when columns are perfectly correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1;
+ QUERY PLAN
+--------------------------------------------------
+ Bitmap Heap Scan on brin_test_bloom
+ Recheck Cond: (a = 1)
+ -> Bitmap Index Scan on brin_test_bloom_a_idx
+ Index Cond: (a = 1)
+(4 rows)
+
+-- Ensure brin index is not used when values are not correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1;
+ QUERY PLAN
+-----------------------------
+ Seq Scan on brin_test_bloom
+ Filter: (b = 1)
+(2 rows)
+
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 1b3c146e4c..dca8e9eb34 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2034,6 +2034,7 @@ ORDER BY 1, 2, 3;
2742 | 16 | @@
3580 | 1 | <
3580 | 1 | <<
+ 3580 | 1 | =
3580 | 2 | &<
3580 | 2 | <=
3580 | 3 | &&
@@ -2097,7 +2098,7 @@ ORDER BY 1, 2, 3;
4000 | 26 | >>
4000 | 27 | >>=
4000 | 28 | ^@
-(125 rows)
+(126 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index daac0ff49d..72c7598c89 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4989,8 +4989,9 @@ List of access methods
List of operator classes
AM | Input type | Storage type | Operator class | Default?
------+------------+--------------+----------------+----------
+ brin | oid | | oid_bloom_ops | no
brin | oid | | oid_minmax_ops | yes
-(1 row)
+(2 rows)
\dAf spgist
List of operator families
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 274130e706..97bf9797de 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%'
(SELECT 1 FROM pg_type as p2
WHERE p2.typname = ('_' || p1.typname)::name AND
p2.typelem = p1.oid and p1.typarray = p2.oid);
- oid | typname
-------+-----------------
+ oid | typname
+------+-----------------------
194 | pg_node_tree
3361 | pg_ndistinct
3402 | pg_dependencies
5017 | pg_mcv_list
-(4 rows)
+ 9034 | pg_brin_bloom_summary
+(5 rows)
-- Make sure typarray points to a varlena array type of our own base
SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 026ea880cd..b49239b1d0 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi
# ----------
test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash
+# ----------
+# Additional BRIN tests
+# ----------
+test: brin_bloom
+
# ----------
# Another group of parallel tests
# ----------
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 979d926119..abce8e180a 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -107,6 +107,7 @@ test: delete
test: namespace
test: prepared_xacts
test: brin
+test: brin_bloom
test: gin
test: gist
test: spgist
diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql
new file mode 100644
index 0000000000..abd5a33deb
--- /dev/null
+++ b/src/test/regress/sql/brin_bloom.sql
@@ -0,0 +1,404 @@
+CREATE TABLE brintest_bloom (byteacol bytea,
+ charcol "char",
+ namecol name,
+ int8col bigint,
+ int2col smallint,
+ int4col integer,
+ textcol text,
+ oidcol oid,
+ float4col real,
+ float8col double precision,
+ macaddrcol macaddr,
+ inetcol inet,
+ cidrcol cidr,
+ bpcharcol character,
+ datecol date,
+ timecol time without time zone,
+ timestampcol timestamp without time zone,
+ timestamptzcol timestamp with time zone,
+ intervalcol interval,
+ timetzcol time with time zone,
+ numericcol numeric,
+ uuidcol uuid,
+ lsncol pg_lsn
+) WITH (fillfactor=10);
+
+INSERT INTO brintest_bloom SELECT
+ repeat(stringu1, 8)::bytea,
+ substr(stringu1, 1, 1)::"char",
+ stringu1::name, 142857 * tenthous,
+ thousand,
+ twothousand,
+ repeat(stringu1, 8),
+ unique1::oid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4/24' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ substr(stringu1, 1, 1)::bpchar,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20+02' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 100;
+
+-- throw in some NULL's and different values
+INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT
+ inet 'fe80::6e40:8ff:fea9:8c46' + tenthous,
+ cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous
+FROM tenk1 ORDER BY thousand, tenthous LIMIT 25;
+
+-- test bloom specific index options
+-- ndistinct must be >= -1.0
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(n_distinct_per_range = -1.1)
+);
+-- false_positive_rate must be between 0.001 and 1.0
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(false_positive_rate = 0.0)
+);
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(false_positive_rate = 1.01)
+);
+
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops,
+ charcol char_bloom_ops,
+ namecol name_bloom_ops,
+ int8col int8_bloom_ops,
+ int2col int2_bloom_ops,
+ int4col int4_bloom_ops,
+ textcol text_bloom_ops,
+ oidcol oid_bloom_ops,
+ float4col float4_bloom_ops,
+ float8col float8_bloom_ops,
+ macaddrcol macaddr_bloom_ops,
+ inetcol inet_bloom_ops,
+ cidrcol inet_bloom_ops,
+ bpcharcol bpchar_bloom_ops,
+ datecol date_bloom_ops,
+ timecol time_bloom_ops,
+ timestampcol timestamp_bloom_ops,
+ timestamptzcol timestamptz_bloom_ops,
+ intervalcol interval_bloom_ops,
+ timetzcol timetz_bloom_ops,
+ numericcol numeric_bloom_ops,
+ uuidcol uuid_bloom_ops,
+ lsncol pg_lsn_bloom_ops
+) with (pages_per_range = 1);
+
+CREATE TABLE brinopers_bloom (colname name, typ text,
+ op text[], value text[], matches int[],
+ check (cardinality(op) = cardinality(value)),
+ check (cardinality(op) = cardinality(matches)));
+
+INSERT INTO brinopers_bloom VALUES
+ ('byteacol', 'bytea',
+ '{=}',
+ '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}',
+ '{1}'),
+ ('charcol', '"char"',
+ '{=}',
+ '{M}',
+ '{6}'),
+ ('namecol', 'name',
+ '{=}',
+ '{MAAAAA}',
+ '{2}'),
+ ('int2col', 'int2',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int2col', 'int4',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int2col', 'int8',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int2',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int4',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int8',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int8col', 'int8',
+ '{=}',
+ '{1257141600}',
+ '{1}'),
+ ('textcol', 'text',
+ '{=}',
+ '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}',
+ '{1}'),
+ ('oidcol', 'oid',
+ '{=}',
+ '{8800}',
+ '{1}'),
+ ('float4col', 'float4',
+ '{=}',
+ '{1}',
+ '{4}'),
+ ('float4col', 'float8',
+ '{=}',
+ '{1}',
+ '{4}'),
+ ('float8col', 'float4',
+ '{=}',
+ '{0}',
+ '{1}'),
+ ('float8col', 'float8',
+ '{=}',
+ '{0}',
+ '{1}'),
+ ('macaddrcol', 'macaddr',
+ '{=}',
+ '{2c:00:2d:00:16:00}',
+ '{2}'),
+ ('inetcol', 'inet',
+ '{=}',
+ '{10.2.14.231/24}',
+ '{1}'),
+ ('inetcol', 'cidr',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('cidrcol', 'inet',
+ '{=}',
+ '{10.2.14/24}',
+ '{2}'),
+ ('cidrcol', 'inet',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('cidrcol', 'cidr',
+ '{=}',
+ '{10.2.14/24}',
+ '{2}'),
+ ('cidrcol', 'cidr',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('bpcharcol', 'bpchar',
+ '{=}',
+ '{W}',
+ '{6}'),
+ ('datecol', 'date',
+ '{=}',
+ '{2009-12-01}',
+ '{1}'),
+ ('timecol', 'time',
+ '{=}',
+ '{02:28:57}',
+ '{1}'),
+ ('timestampcol', 'timestamp',
+ '{=}',
+ '{1964-03-24 19:26:45}',
+ '{1}'),
+ ('timestampcol', 'timestamptz',
+ '{=}',
+ '{1964-03-24 19:26:45}',
+ '{1}'),
+ ('timestamptzcol', 'timestamptz',
+ '{=}',
+ '{1972-10-19 09:00:00-07}',
+ '{1}'),
+ ('intervalcol', 'interval',
+ '{=}',
+ '{1 mons 13 days 12:24}',
+ '{1}'),
+ ('timetzcol', 'timetz',
+ '{=}',
+ '{01:35:50+02}',
+ '{2}'),
+ ('numericcol', 'numeric',
+ '{=}',
+ '{2268164.347826086956521739130434782609}',
+ '{1}'),
+ ('uuidcol', 'uuid',
+ '{=}',
+ '{52225222-5222-5222-5222-522252225222}',
+ '{1}'),
+ ('lsncol', 'pg_lsn',
+ '{=, IS, IS NOT}',
+ '{44/455222, NULL, NULL}',
+ '{1, 25, 100}');
+
+DO $x$
+DECLARE
+ r record;
+ r2 record;
+ cond text;
+ idx_ctids tid[];
+ ss_ctids tid[];
+ count int;
+ plan_ok bool;
+ plan_line text;
+BEGIN
+ FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP
+
+ -- prepare the condition
+ IF r.value IS NULL THEN
+ cond := format('%I %s %L', r.colname, r.oper, r.value);
+ ELSE
+ cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ);
+ END IF;
+
+ -- run the query using the brin index
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get bitmap indexscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond)
+ INTO idx_ctids;
+
+ -- run the query using a seqscan
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get seqscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond)
+ INTO ss_ctids;
+
+ -- make sure both return the same results
+ count := array_length(idx_ctids, 1);
+
+ IF NOT (count = array_length(ss_ctids, 1) AND
+ idx_ctids @> ss_ctids AND
+ idx_ctids <@ ss_ctids) THEN
+ -- report the results of each scan to make the differences obvious
+ RAISE WARNING 'something not right in %: count %', r, count;
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP
+ RAISE NOTICE 'seqscan: %', r2;
+ END LOOP;
+
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP
+ RAISE NOTICE 'bitmapscan: %', r2;
+ END LOOP;
+ END IF;
+
+ -- make sure we found expected number of matches
+ IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF;
+ END LOOP;
+END;
+$x$;
+
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+
+INSERT INTO brintest_bloom SELECT
+ repeat(stringu1, 42)::bytea,
+ substr(stringu1, 1, 1)::"char",
+ stringu1::name, 142857 * tenthous,
+ thousand,
+ twothousand,
+ repeat(stringu1, 42),
+ unique1::oid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ substr(stringu1, 1, 1)::bpchar,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5;
+
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+VACUUM brintest_bloom; -- force a summarization cycle in brinidx
+
+UPDATE brintest_bloom SET int8col = int8col * int4col;
+UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL;
+
+-- Tests for brin_summarize_new_values
+SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index
+SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
+SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected
+
+-- Tests for brin_desummarize_range
+SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+SELECT brin_desummarize_range('brinidx_bloom', 100000000);
+
+-- Test brin_summarize_range
+CREATE TABLE brin_summarize_bloom (
+ value int
+) WITH (fillfactor=10, autovacuum_enabled=false);
+CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2);
+-- Fill a few pages
+DO $$
+DECLARE curtid tid;
+BEGIN
+ LOOP
+ INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid;
+ EXIT WHEN curtid > tid '(2, 0)';
+ END LOOP;
+END;
+$$;
+
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 0);
+-- nothing: already summarized
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 1);
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 2);
+-- nothing: page doesn't exist in table
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295);
+-- invalid block number values
+SELECT brin_summarize_range('brin_summarize_bloom_idx', -1);
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296);
+
+
+-- test brin cost estimates behave sanely based on correlation of values
+CREATE TABLE brin_test_bloom (a INT, b INT);
+INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x);
+CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2);
+CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2);
+VACUUM ANALYZE brin_test_bloom;
+
+-- Ensure brin index is used when columns are perfectly correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1;
+-- Ensure brin index is not used when values are not correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1;
--
2.25.4
From 87f5f02c4c0df16715e7ca5064f22bc6c1e64f8c Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 12 Sep 2020 15:07:49 +0200
Subject: [PATCH 4/5] BRIN minmax-multi indexes
Adds a BRIN minmax-multi opclass, summarizing each range into a list of
intervals, allowing more efficient handling of cases where the minmax
opclasses quickly degrade.
Instead of representing each range with a single interval, minmax-multi
opclasses store a list of intervals and points. This allows effective
handling of outliers and poorly correlated values.
The number of intervals/points per range may be configured using an
opclass parameter values_per_range. When building the summary, the
interval are merged based on distance, determined by the new support
BRIN procedure.
Author: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com
---
doc/src/sgml/brin.sgml | 252 +-
doc/src/sgml/ref/create_index.sgml | 11 +
src/backend/access/brin/Makefile | 1 +
src/backend/access/brin/brin_minmax_multi.c | 2390 +++++++++++++++++++
src/backend/access/brin/brin_tuple.c | 17 +
src/include/access/brin.h | 1 +
src/include/access/brin_internal.h | 3 +
src/include/access/brin_tuple.h | 4 +
src/include/access/transam.h | 2 +-
src/include/catalog/pg_amop.dat | 544 +++++
src/include/catalog/pg_amproc.dat | 600 ++++-
src/include/catalog/pg_opclass.dat | 57 +
src/include/catalog/pg_opfamily.dat | 28 +
src/include/catalog/pg_proc.dat | 80 +
src/include/catalog/pg_type.dat | 7 +
src/test/regress/expected/brin_multi.out | 421 ++++
src/test/regress/expected/psql.out | 13 +-
src/test/regress/expected/type_sanity.out | 7 +-
src/test/regress/parallel_schedule | 2 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/brin_multi.sql | 371 +++
21 files changed, 4793 insertions(+), 19 deletions(-)
create mode 100644 src/backend/access/brin/brin_minmax_multi.c
create mode 100644 src/test/regress/expected/brin_multi.out
create mode 100644 src/test/regress/sql/brin_multi.sql
diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml
index 577dd18c49..3caa30f104 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (date,date)</literal></entry></row>
<row><entry><literal>>= (date,date)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry>
+ <entry><literal>= (date,date)</literal></entry>
+ </row>
+ <row><entry><literal>< (date,date)</literal></entry></row>
+ <row><entry><literal><= (date,date)</literal></entry></row>
+ <row><entry><literal>> (date,date)</literal></entry></row>
+ <row><entry><literal>>= (date,date)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>float4_bloom_ops</literal></entry>
<entry><literal>= (float4,float4)</literal></entry>
@@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (float4,float4)</literal></entry></row>
<row><entry><literal>>= (float4,float4)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry>
+ <entry><literal>= (float4,float4)</literal></entry>
+ </row>
+ <row><entry><literal>< (float4,float4)</literal></entry></row>
+ <row><entry><literal>> (float4,float4)</literal></entry></row>
+ <row><entry><literal><= (float4,float4)</literal></entry></row>
+ <row><entry><literal>>= (float4,float4)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>float8_bloom_ops</literal></entry>
<entry><literal>= (float8,float8)</literal></entry>
@@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (float8,float8)</literal></entry></row>
<row><entry><literal>>= (float8,float8)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry>
+ <entry><literal>= (float8,float8)</literal></entry>
+ </row>
+ <row><entry><literal>< (float8,float8)</literal></entry></row>
+ <row><entry><literal><= (float8,float8)</literal></entry></row>
+ <row><entry><literal>> (float8,float8)</literal></entry></row>
+ <row><entry><literal>>= (float8,float8)</literal></entry></row>
+
<row>
<entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry>
<entry><literal><< (inet,inet)</literal></entry>
@@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (inet,inet)</literal></entry></row>
<row><entry><literal>>= (inet,inet)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry>
+ <entry><literal>= (inet,inet)</literal></entry>
+ </row>
+ <row><entry><literal>< (inet,inet)</literal></entry></row>
+ <row><entry><literal><= (inet,inet)</literal></entry></row>
+ <row><entry><literal>> (inet,inet)</literal></entry></row>
+ <row><entry><literal>>= (inet,inet)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>int2_bloom_ops</literal></entry>
<entry><literal>= (int2,int2)</literal></entry>
@@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (int2,int2)</literal></entry></row>
<row><entry><literal>>= (int2,int2)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry>
+ <entry><literal>= (int2,int2)</literal></entry>
+ </row>
+ <row><entry><literal>< (int2,int2)</literal></entry></row>
+ <row><entry><literal>> (int2,int2)</literal></entry></row>
+ <row><entry><literal><= (int2,int2)</literal></entry></row>
+ <row><entry><literal>>= (int2,int2)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>int4_bloom_ops</literal></entry>
<entry><literal>= (int4,int4)</literal></entry>
@@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (int4,int4)</literal></entry></row>
<row><entry><literal>>= (int4,int4)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry>
+ <entry><literal>= (int4,int4)</literal></entry>
+ </row>
+ <row><entry><literal>< (int4,int4)</literal></entry></row>
+ <row><entry><literal>> (int4,int4)</literal></entry></row>
+ <row><entry><literal><= (int4,int4)</literal></entry></row>
+ <row><entry><literal>>= (int4,int4)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>int8_bloom_ops</literal></entry>
<entry><literal>= (bigint,bigint)</literal></entry>
@@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (bigint,bigint)</literal></entry></row>
<row><entry><literal>>= (bigint,bigint)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry>
+ <entry><literal>= (bigint,bigint)</literal></entry>
+ </row>
+ <row><entry><literal>< (bigint,bigint)</literal></entry></row>
+ <row><entry><literal>> (bigint,bigint)</literal></entry></row>
+ <row><entry><literal><= (bigint,bigint)</literal></entry></row>
+ <row><entry><literal>>= (bigint,bigint)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>interval_bloom_ops</literal></entry>
<entry><literal>= (interval,interval)</literal></entry>
@@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (interval,interval)</literal></entry></row>
<row><entry><literal>>= (interval,interval)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry>
+ <entry><literal>= (interval,interval)</literal></entry>
+ </row>
+ <row><entry><literal>< (interval,interval)</literal></entry></row>
+ <row><entry><literal><= (interval,interval)</literal></entry></row>
+ <row><entry><literal>> (interval,interval)</literal></entry></row>
+ <row><entry><literal>>= (interval,interval)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>macaddr_bloom_ops</literal></entry>
<entry><literal>= (macaddr,macaddr)</literal></entry>
@@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (macaddr,macaddr)</literal></entry></row>
<row><entry><literal>>= (macaddr,macaddr)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry>
+ <entry><literal>= (macaddr,macaddr)</literal></entry>
+ </row>
+ <row><entry><literal>< (macaddr,macaddr)</literal></entry></row>
+ <row><entry><literal><= (macaddr,macaddr)</literal></entry></row>
+ <row><entry><literal>> (macaddr,macaddr)</literal></entry></row>
+ <row><entry><literal>>= (macaddr,macaddr)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry>
<entry><literal>= (macaddr8,macaddr8)</literal></entry>
@@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (macaddr8,macaddr8)</literal></entry></row>
<row><entry><literal>>= (macaddr8,macaddr8)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry>
+ <entry><literal>= (macaddr8,macaddr8)</literal></entry>
+ </row>
+ <row><entry><literal>< (macaddr8,macaddr8)</literal></entry></row>
+ <row><entry><literal><= (macaddr8,macaddr8)</literal></entry></row>
+ <row><entry><literal>> (macaddr8,macaddr8)</literal></entry></row>
+ <row><entry><literal>>= (macaddr8,macaddr8)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>name_bloom_ops</literal></entry>
<entry><literal>= (name,name)</literal></entry>
@@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (numeric,numeric)</literal></entry></row>
<row><entry><literal>>= (numeric,numeric)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry>
+ <entry><literal>= (numeric,numeric)</literal></entry>
+ </row>
+ <row><entry><literal>< (numeric,numeric)</literal></entry></row>
+ <row><entry><literal><= (numeric,numeric)</literal></entry></row>
+ <row><entry><literal>> (numeric,numeric)</literal></entry></row>
+ <row><entry><literal>>= (numeric,numeric)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>oid_bloom_ops</literal></entry>
<entry><literal>= (oid,oid)</literal></entry>
@@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (oid,oid)</literal></entry></row>
<row><entry><literal>>= (oid,oid)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry>
+ <entry><literal>= (oid,oid)</literal></entry>
+ </row>
+ <row><entry><literal>< (oid,oid)</literal></entry></row>
+ <row><entry><literal>> (oid,oid)</literal></entry></row>
+ <row><entry><literal><= (oid,oid)</literal></entry></row>
+ <row><entry><literal>>= (oid,oid)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry>
<entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
@@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (pg_lsn,pg_lsn)</literal></entry></row>
<row><entry><literal>>= (pg_lsn,pg_lsn)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry>
+ <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
+ </row>
+ <row><entry><literal>< (pg_lsn,pg_lsn)</literal></entry></row>
+ <row><entry><literal>> (pg_lsn,pg_lsn)</literal></entry></row>
+ <row><entry><literal><= (pg_lsn,pg_lsn)</literal></entry></row>
+ <row><entry><literal>>= (pg_lsn,pg_lsn)</literal></entry></row>
+
<row>
<entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry>
<entry><literal>= (anyrange,anyrange)</literal></entry>
@@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (tid,tid)</literal></entry></row>
<row><entry><literal>>= (tid,tid)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry>
+ <entry><literal>= (tid,tid)</literal></entry>
+ </row>
+ <row><entry><literal>< (tid,tid)</literal></entry></row>
+ <row><entry><literal>> (tid,tid)</literal></entry></row>
+ <row><entry><literal><= (tid,tid)</literal></entry></row>
+ <row><entry><literal>>= (tid,tid)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>timestamp_bloom_ops</literal></entry>
<entry><literal>= (timestamp,timestamp)</literal></entry>
@@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timestamp,timestamp)</literal></entry></row>
<row><entry><literal>>= (timestamp,timestamp)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry>
+ <entry><literal>= (timestamp,timestamp)</literal></entry>
+ </row>
+ <row><entry><literal>< (timestamp,timestamp)</literal></entry></row>
+ <row><entry><literal><= (timestamp,timestamp)</literal></entry></row>
+ <row><entry><literal>> (timestamp,timestamp)</literal></entry></row>
+ <row><entry><literal>>= (timestamp,timestamp)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry>
<entry><literal>= (timestamptz,timestamptz)</literal></entry>
@@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timestamptz,timestamptz)</literal></entry></row>
<row><entry><literal>>= (timestamptz,timestamptz)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry>
+ <entry><literal>= (timestamptz,timestamptz)</literal></entry>
+ </row>
+ <row><entry><literal>< (timestamptz,timestamptz)</literal></entry></row>
+ <row><entry><literal><= (timestamptz,timestamptz)</literal></entry></row>
+ <row><entry><literal>> (timestamptz,timestamptz)</literal></entry></row>
+ <row><entry><literal>>= (timestamptz,timestamptz)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>time_bloom_ops</literal></entry>
<entry><literal>= (time,time)</literal></entry>
@@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (time,time)</literal></entry></row>
<row><entry><literal>>= (time,time)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry>
+ <entry><literal>= (time,time)</literal></entry>
+ </row>
+ <row><entry><literal>< (time,time)</literal></entry></row>
+ <row><entry><literal><= (time,time)</literal></entry></row>
+ <row><entry><literal>> (time,time)</literal></entry></row>
+ <row><entry><literal>>= (time,time)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>timetz_bloom_ops</literal></entry>
<entry><literal>= (timetz,timetz)</literal></entry>
@@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timetz,timetz)</literal></entry></row>
<row><entry><literal>>= (timetz,timetz)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry>
+ <entry><literal>= (timetz,timetz)</literal></entry>
+ </row>
+ <row><entry><literal>< (timetz,timetz)</literal></entry></row>
+ <row><entry><literal><= (timetz,timetz)</literal></entry></row>
+ <row><entry><literal>> (timetz,timetz)</literal></entry></row>
+ <row><entry><literal>>= (timetz,timetz)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>uuid_bloom_ops</literal></entry>
<entry><literal>= (uuid,uuid)</literal></entry>
@@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (uuid,uuid)</literal></entry></row>
<row><entry><literal>>= (uuid,uuid)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry>
+ <entry><literal>= (uuid,uuid)</literal></entry>
+ </row>
+ <row><entry><literal>< (uuid,uuid)</literal></entry></row>
+ <row><entry><literal>> (uuid,uuid)</literal></entry></row>
+ <row><entry><literal><= (uuid,uuid)</literal></entry></row>
+ <row><entry><literal>>= (uuid,uuid)</literal></entry></row>
+
<row>
<entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry>
<entry><literal>= (varbit,varbit)</literal></entry>
@@ -654,13 +825,14 @@ typedef struct BrinOpcInfo
</varlistentry>
</variablelist>
- The core distribution includes support for two types of operator classes:
- minmax and inclusion. Operator class definitions using them are shipped for
- in-core data types as appropriate. Additional operator classes can be
- defined by the user for other data types using equivalent definitions,
- without having to write any source code; appropriate catalog entries being
- declared is enough. Note that assumptions about the semantics of operator
- strategies are embedded in the support functions' source code.
+ The core distribution includes support for four types of operator classes:
+ minmax, multi-minmax, inclusion and bloom. Operator class definitions
+ using them are shipped for in-core data types as appropriate. Additional
+ operator classes can be defined by the user for other data types using
+ equivalent definitions, without having to write any source code;
+ appropriate catalog entries being declared is enough. Note that
+ assumptions about the semantics of operator strategies are embedded in the
+ support functions' source code.
</para>
<para>
@@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo
and return a hash of the value.
</para>
+ <para>
+ The multi-minmax operator class is also intended for data types implementing
+ a totally ordered sets, and may be seen as a simple extension of the minmax
+ operator class. While minmax operator class summarizes values from each block
+ range into a single contiguous interval, multi-minmax allows summarization
+ into multiple smaller intervals to improve handling of outlier values.
+ It is possible to use the multi-minmax support procedures alongside the
+ corresponding operators, as shown in
+ <xref linkend="brin-extensibility-multi-minmax-table"/>.
+ All operator class members (procedures and operators) are mandatory.
+ </para>
+
+ <table id="brin-extensibility-multi-minmax-table">
+ <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Operator class member</entry>
+ <entry>Object</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Support Procedure 1</entry>
+ <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 2</entry>
+ <entry>internal function <function>brin_minmax_multi_add_value()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 3</entry>
+ <entry>internal function <function>brin_minmax_multi_consistent()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 4</entry>
+ <entry>internal function <function>brin_minmax_multi_union()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 11</entry>
+ <entry>function to compute distance between two values (length of a range)</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 1</entry>
+ <entry>operator less-than</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 2</entry>
+ <entry>operator less-than-or-equal-to</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 3</entry>
+ <entry>operator equal-to</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 4</entry>
+ <entry>operator greater-than-or-equal-to</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 5</entry>
+ <entry>operator greater-than</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
<para>
Both minmax and inclusion operator classes support cross-data-type
operators, though with these the dependencies become more complicated.
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 9c90d451a7..d86e3ed8f2 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -586,6 +586,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>values_per_range</literal></term>
+ <listitem>
+ <para>
+ Defines the maximum number of values stored by <acronym>BRIN</acronym>
+ minmax indexes to summarize a block range. Each value may represent
+ eiter a point, or boundary of an interval. Values must be be between
+ 16 and 256, and the default value is 32.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect2>
diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile
index 6b56131215..a386cb71f1 100644
--- a/src/backend/access/brin/Makefile
+++ b/src/backend/access/brin/Makefile
@@ -17,6 +17,7 @@ OBJS = \
brin_bloom.o \
brin_inclusion.o \
brin_minmax.o \
+ brin_minmax_multi.o \
brin_pageops.o \
brin_revmap.o \
brin_tuple.o \
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
new file mode 100644
index 0000000000..227927fcf5
--- /dev/null
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -0,0 +1,2390 @@
+/*
+ * brin_minmax_multi.c
+ * Implementation of Multi Min/Max opclass for BRIN
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * Implements a variant of minmax opclass, where the summary is composed of
+ * multiple smaller intervals. This allows us to handle outliers, which
+ * usually makes the simple minmax opclass inefficient.
+ *
+ * Consider for example page range with simple minmax interval [1000,2000],
+ * and assume a new row gets inserted into the range with value 1000000.
+ * Due to that the interval gets [1000,1000000]. I.e. the minmax interval
+ * got 1000x wider and won't be useful to eliminate scan keys between 2001
+ * and 1000000.
+ *
+ * With multi-minmax opclass, we may have [1000,2000] interval initially,
+ * but after adding the new row we start tracking it as two interval:
+ *
+ * [1000,2000] and [1000000,1000000]
+ *
+ * This allow us to still eliminate the page range when the scan keys hit
+ * the gap between 2000 and 1000000, making it useful in cases when the
+ * simple minmax opclass gets inefficient.
+ *
+ * The number of intervals tracked per page range is somewhat flexible.
+ * What is restricted is the number of values per page range, and the limit
+ * is currently 64 (see values_per_range reloption). Collapsed intervals
+ * (with equal minimum and maximum value) are stored as a single value,
+ * while regular intervals require two values.
+ *
+ * When the number of values gets too high (by adding new values to the
+ * summary), we merge some of the intervals to free space for more values.
+ * This is done in a greedy way - we simply pick the two closest intervals,
+ * merge them, and repeat this until the number of values to store gets
+ * sufficiently low (below 75% of maximum values), but that is mostly
+ * arbitrary threshold and may be changed easily).
+ *
+ * To pick the closest intervals we use the "distance" support procedure,
+ * which measures space between two ranges (i.e. length of an interval).
+ * The computed value may be an approximation - in the worst case we will
+ * merge two ranges that are slightly less optimal at that step, but the
+ * index should still produce correct results.
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/brin/brin_minmax_multi.c
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/brin.h"
+#include "access/brin_internal.h"
+#include "access/brin_tuple.h"
+#include "access/reloptions.h"
+#include "access/stratnum.h"
+#include "access/htup_details.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_amop.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/datum.h"
+#include "utils/inet.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/numeric.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+#include "utils/uuid.h"
+
+/*
+ * Additional SQL level support functions
+ *
+ * Procedure numbers must not use values reserved for BRIN itself; see
+ * brin_internal.h.
+ */
+#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */
+#define PROCNUM_DISTANCE 11 /* required, distance between values*/
+
+/*
+ * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays
+ * (Must be equal to minimum of private procnums).
+ */
+#define PROCNUM_BASE 11
+
+
+typedef struct MinmaxMultiOpaque
+{
+ FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS];
+ bool extra_proc_missing[MINMAX_MAX_PROCNUMS];
+ Oid cached_subtype;
+ FmgrInfo strategy_procinfos[BTMaxStrategyNumber];
+} MinmaxMultiOpaque;
+
+/*
+ * Storage type for BRIN's minmax reloptions
+ */
+typedef struct MinMaxOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int valuesPerRange; /* number of values per range */
+} MinMaxOptions;
+
+#define MINMAX_DEFAULT_VALUES_PER_PAGE 32
+
+#define MinMaxGetValuesPerRange(opts) \
+ ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \
+ ((MinMaxOptions *) (opts))->valuesPerRange : \
+ MINMAX_DEFAULT_VALUES_PER_PAGE)
+
+
+
+/*
+ * The summary of multi-minmax indexes has two representations - Ranges for
+ * convenient processing, and SerializedRanges for storage in bytea value.
+ *
+ * The Ranges struct stores the boundary values in a single array, but we
+ * treat regular and single-point ranges differently to save space. For
+ * regular ranges (with different boundary values) we have to store both
+ * values, while for "single-point ranges" we only need to save one value.
+ *
+ * The 'values' array stores boundary values for regular ranges first (there
+ * are 2*nranges values to store), and then the nvalues boundary values for
+ * single-point ranges. That is, we have (2*nranges + nvalues) boundary
+ * values in the array.
+ *
+ * +---------------------------------+-------------------------------+
+ * | ranges (sorted pairs of values) | sorted values (single points) |
+ * +---------------------------------+-------------------------------+
+ *
+ * This allows us to quickly add new values, and store outliers without
+ * making the other ranges very wide.
+ *
+ * We never store more than maxvalues values (as set by values_per_range
+ * reloption). If needed we merge some of the ranges.
+ *
+ * To minimize palloc overhead, we always allocate the full array with
+ * space for maxvalues elements. This should be fine as long as the
+ * maxvalues is reasonably small (64 seems fine), which is the case
+ * thanks to values_per_range reloption being limited to 256.
+ */
+typedef struct Ranges
+{
+ Oid typid;
+
+ /* (2*nranges + nvalues) <= maxvalues */
+ int nranges; /* number of ranges in the array (stored) */
+ int nvalues; /* number of values in the data array (all) */
+ int maxvalues; /* maximum number of values (reloption) */
+
+ /* values stored for this range - either raw values, or ranges */
+ Datum values[FLEXIBLE_ARRAY_MEMBER];
+} Ranges;
+
+/*
+ * On-disk the summary is stored as a bytea value, represented by the
+ * SerializedRanges structure. It has a 4B varlena header, so can treated
+ * as varlena directly.
+ *
+ * See range_serialize/range_deserialize methods for serialization details.
+ */
+typedef struct SerializedRanges
+{
+ /* varlena header (do not touch directly!) */
+ int32 vl_len_;
+
+ /* type of values stored in the data array */
+ Oid typid;
+
+ /* (2*nranges + nvalues) <= maxvalues */
+ int nranges; /* number of ranges in the array (stored) */
+ int nvalues; /* number of values in the data array (all) */
+ int maxvalues; /* maximum number of values (reloption) */
+
+ /* contains the actual data */
+ char data[FLEXIBLE_ARRAY_MEMBER];
+} SerializedRanges;
+
+static SerializedRanges *range_serialize(Ranges *range);
+
+static Ranges *range_deserialize(SerializedRanges *range);
+
+/* Cache for support and strategy procesures. */
+
+static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno,
+ uint16 procnum);
+
+static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc,
+ uint16 attno, Oid subtype, uint16 strategynum);
+
+
+/*
+ * minmax_multi_init
+ * Initialize the deserialized range list, allocate all the memory.
+ *
+ * This is only in-memory representation of the ranges, so we allocate
+ * everything enough space for the maximum number of values (so as not
+ * to have to do repallocs as the ranges grow).
+ */
+static Ranges *
+minmax_multi_init(int maxvalues)
+{
+ Size len;
+ Ranges * ranges;
+
+ Assert(maxvalues > 0);
+
+ len = offsetof(Ranges, values); /* fixed header */
+ len += maxvalues * sizeof(Datum); /* Datum values */
+
+ ranges = (Ranges *) palloc0(len);
+
+ ranges->maxvalues = maxvalues;
+
+ return ranges;
+}
+
+/*
+ * range_serialize
+ * Serialize the in-memory representation into a compact varlena value.
+ *
+ * Simply copy the header and then also the individual values, as stored
+ * in the in-memory value array.
+ */
+static SerializedRanges *
+range_serialize(Ranges *range)
+{
+ Size len;
+ int nvalues;
+ SerializedRanges *serialized;
+ Oid typid;
+ int typlen;
+ bool typbyval;
+
+ int i;
+ char *ptr;
+
+ /* simple sanity checks */
+ Assert(range->nranges >= 0);
+ Assert(range->nvalues >= 0);
+ Assert(range->maxvalues > 0);
+
+ /* see how many Datum values we actually have */
+ nvalues = 2*range->nranges + range->nvalues;
+
+ Assert(2*range->nranges + range->nvalues <= range->maxvalues);
+
+ typid = range->typid;
+ typbyval = get_typbyval(typid);
+ typlen = get_typlen(typid);
+
+ /* header is always needed */
+ len = offsetof(SerializedRanges,data);
+
+ /*
+ * The space needed depends on data type - for fixed-length data types
+ * (by-value and some by-reference) it's pretty simple, just multiply
+ * (attlen * nvalues) and we're done. For variable-length by-reference
+ * types we need to actually walk all the values and sum the lengths.
+ */
+ if (typlen == -1) /* varlena */
+ {
+ int i;
+ for (i = 0; i < nvalues; i++)
+ {
+ len += VARSIZE_ANY(range->values[i]);
+ }
+ }
+ else if (typlen == -2) /* cstring */
+ {
+ int i;
+ for (i = 0; i < nvalues; i++)
+ {
+ /* don't forget to include the null terminator ;-) */
+ len += strlen(DatumGetPointer(range->values[i])) + 1;
+ }
+ }
+ else /* fixed-length types (even by-reference) */
+ {
+ Assert(typlen > 0);
+ len += nvalues * typlen;
+ }
+
+ /*
+ * Allocate the serialized object, copy the basic information. The
+ * serialized object is a varlena, so update the header.
+ */
+ serialized = (SerializedRanges *) palloc0(len);
+ SET_VARSIZE(serialized, len);
+
+ serialized->typid = typid;
+ serialized->nranges = range->nranges;
+ serialized->nvalues = range->nvalues;
+ serialized->maxvalues = range->maxvalues;
+
+ /*
+ * And now copy also the boundary values (like the length calculation
+ * this depends on the particular data type).
+ */
+ ptr = serialized->data; /* start of the serialized data */
+
+ for (i = 0; i < nvalues; i++)
+ {
+ if (typbyval) /* simple by-value data types */
+ {
+ memcpy(ptr, &range->values[i], typlen);
+ ptr += typlen;
+ }
+ else if (typlen > 0) /* fixed-length by-ref types */
+ {
+ memcpy(ptr, DatumGetPointer(range->values[i]), typlen);
+ ptr += typlen;
+ }
+ else if (typlen == -1) /* varlena */
+ {
+ int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i]));
+ memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
+ ptr += tmp;
+ }
+ else if (typlen == -2) /* cstring */
+ {
+ int tmp = strlen(DatumGetPointer(range->values[i])) + 1;
+ memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
+ ptr += tmp;
+ }
+
+ /* make sure we haven't overflown the buffer end */
+ Assert(ptr <= ((char *)serialized + len));
+ }
+
+ /* exact size */
+ Assert(ptr == ((char *)serialized + len));
+
+ return serialized;
+}
+
+/*
+ * range_deserialize
+ * Serialize the in-memory representation into a compact varlena value.
+ *
+ * Simply copy the header and then also the individual values, as stored
+ * in the in-memory value array.
+ */
+static Ranges *
+range_deserialize(SerializedRanges *serialized)
+{
+ int i,
+ nvalues;
+ char *ptr;
+ bool typbyval;
+ int typlen;
+
+ Ranges *range;
+
+ Assert(serialized->nranges >= 0);
+ Assert(serialized->nvalues >= 0);
+ Assert(serialized->maxvalues > 0);
+
+ nvalues = 2*serialized->nranges + serialized->nvalues;
+
+ Assert(nvalues <= serialized->maxvalues);
+
+ range = minmax_multi_init(serialized->maxvalues);
+
+ /* copy the header info */
+ range->nranges = serialized->nranges;
+ range->nvalues = serialized->nvalues;
+ range->maxvalues = serialized->maxvalues;
+ range->typid = serialized->typid;
+
+ typbyval = get_typbyval(serialized->typid);
+ typlen = get_typlen(serialized->typid);
+
+ /*
+ * And now deconstruct the values into Datum array. We don't need
+ * to copy the values and will instead just point the values to the
+ * serialized varlena value (assuming it will be kept around).
+ */
+ ptr = serialized->data;
+
+ for (i = 0; i < nvalues; i++)
+ {
+ if (typbyval) /* simple by-value data types */
+ {
+ memcpy(&range->values[i], ptr, typlen);
+ ptr += typlen;
+ }
+ else if (typlen > 0) /* fixed-length by-ref types */
+ {
+ /* no copy, just set the value to the pointer */
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += typlen;
+ }
+ else if (typlen == -1) /* varlena */
+ {
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += VARSIZE_ANY(DatumGetPointer(range->values[i]));
+ }
+ else if (typlen == -2) /* cstring */
+ {
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += strlen(DatumGetPointer(range->values[i])) + 1;
+ }
+
+ /* make sure we haven't overflown the buffer end */
+ Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized)));
+ }
+
+ /* should have consumed the whole input value exactly */
+ Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized)));
+
+ /* return the deserialized value */
+ return range;
+}
+
+typedef struct compare_context
+{
+ FmgrInfo *cmpFn;
+ Oid colloid;
+} compare_context;
+
+/*
+ * Used to represent ranges expanded during merging and combining (to
+ * reduce number of boundary values to store).
+ *
+ * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to
+ * something ExpandedRange or so.
+ */
+typedef struct CombineRange
+{
+ Datum minval; /* lower boundary */
+ Datum maxval; /* upper boundary */
+ bool collapsed; /* true if minval==maxval */
+} CombineRange;
+
+/*
+ * compare_combine_ranges
+ * Compare the combine ranges - first by minimum, then by maximum.
+ *
+ * We do guarantee that ranges in a single Range object do not overlap,
+ * so it may seem strange that we don't order just by minimum. But when
+ * merging two Ranges (which happens in the union function), the ranges
+ * may in fact overlap. So we do compare both.
+ */
+static int
+compare_combine_ranges(const void *a, const void *b, void *arg)
+{
+ CombineRange *ra = (CombineRange *)a;
+ CombineRange *rb = (CombineRange *)b;
+ Datum r;
+
+ compare_context *cxt = (compare_context *)arg;
+
+ /* first compare minvals */
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, ra->minval, rb->minval);
+
+ if (DatumGetBool(r))
+ return -1;
+
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, rb->minval, ra->minval);
+
+ if (DatumGetBool(r))
+ return 1;
+
+ /* then compare maxvals */
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, ra->maxval, rb->maxval);
+
+ if (DatumGetBool(r))
+ return -1;
+
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, rb->maxval, ra->maxval);
+
+ if (DatumGetBool(r))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * range_contains_value
+ * See if the new value is already contained in the range list.
+ *
+ * We first inspect the list of intervals. We use a small trick - we check
+ * the value against min/max of the whole range (min of the first interval,
+ * max of the last one) first, and only inspect the individual intervals if
+ * this passes.
+ *
+ * If the value matches none of the intervals, we check the exact values.
+ * We simply loop through them and invoke equality operator on them.
+ *
+ * XXX This might benefit from the fact that both the intervals and exact
+ * values are sorted - we might do bsearch or something. Currently that
+ * does not make much difference (there are only ~32 intervals), but if
+ * this gets increased and/or the comparator function is more expensive,
+ * it might be a huge win.
+ */
+static bool
+range_contains_value(BrinDesc *bdesc, Oid colloid,
+ AttrNumber attno, Form_pg_attribute attr,
+ Ranges *ranges, Datum newval)
+{
+ int i;
+ FmgrInfo *cmpLessFn;
+ FmgrInfo *cmpGreaterFn;
+ FmgrInfo *cmpEqualFn;
+ Oid typid = attr->atttypid;
+
+ /*
+ * First inspect the ranges, if there are any. We first check the whole
+ * range, and only when there's still a chance of getting a match we
+ * inspect the individual ranges.
+ */
+ if (ranges->nranges > 0)
+ {
+ Datum compar;
+ bool match = true;
+
+ Datum minvalue = ranges->values[0];
+ Datum maxvalue = ranges->values[2*ranges->nranges - 1];
+
+ /*
+ * Otherwise, need to compare the new value with boundaries of all
+ * the ranges. First check if it's less than the absolute minimum,
+ * which is the first value in the array.
+ */
+ cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTLessStrategyNumber);
+ compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue);
+
+ /* smaller than the smallest value in the range list */
+ if (DatumGetBool(compar))
+ match = false;
+
+ /*
+ * And now compare it to the existing maximum (last value in the
+ * data array). But only if we haven't already ruled out a possible
+ * match in the minvalue check.
+ */
+ if (match)
+ {
+ cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTGreaterStrategyNumber);
+ compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue);
+
+ if (DatumGetBool(compar))
+ match = false;
+ }
+
+ /*
+ * So it's in the general range, but is it actually covered by any
+ * of the ranges? Repeat the check for each range.
+ *
+ * XXX We simply walk the ranges sequentially, but maybe we could
+ * further leverage the ordering and non-overlap and use bsearch to
+ * speed this up a bit.
+ */
+ for (i = 0; i < ranges->nranges && match; i++)
+ {
+ /* copy the min/max values from the ranges */
+ minvalue = ranges->values[2*i];
+ maxvalue = ranges->values[2*i+1];
+
+ /*
+ * Otherwise, need to compare the new value with boundaries of all
+ * the ranges. First check if it's less than the absolute minimum,
+ * which is the first value in the array.
+ */
+ compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue);
+
+ /* smaller than the smallest value in this range */
+ if (DatumGetBool(compar))
+ continue;
+
+ compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue);
+
+ /* larger than the largest value in this range */
+ if (DatumGetBool(compar))
+ continue;
+
+ /* hey, we found a matching row */
+ return true;
+ }
+ }
+
+ cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTEqualStrategyNumber);
+
+ /*
+ * We're done with the ranges, now let's inspect the exact values.
+ *
+ * XXX Again, we do sequentially search the values - consider leveraging
+ * the ordering of values to improve performance.
+ */
+ for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++)
+ {
+ Datum compar;
+
+ compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]);
+
+ /* found an exact match */
+ if (DatumGetBool(compar))
+ return true;
+ }
+
+ /* the value is not covered by this BRIN tuple */
+ return false;
+}
+
+/*
+ * insert_value
+ * Adds a new value into the single-point part, while maintaining ordering.
+ *
+ * The function inserts the new value to the right place in the single-point
+ * part of the range. It assumes there's enough free space, and then does
+ * essentially an insert-sort.
+ *
+ * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that
+ * only the first nvalues are used.
+ */
+static void
+insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues,
+ Datum newvalue)
+{
+ int i;
+ Datum lt;
+
+ /* If there are no values yet, store the new one and we're done. */
+ if (!nvalues)
+ {
+ values[0] = newvalue;
+ return;
+ }
+
+ /*
+ * A common case is that the new value is entirely out of the existing
+ * range, i.e. it's either smaller or larger than all previous values.
+ * So we check and handle this case first - first we check the larger
+ * case, because in that case we can just append the value to the end
+ * of the array and we're done.
+ */
+
+ /* Is it greater than all existing values in the array? */
+ lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue);
+ if (DatumGetBool(lt))
+ {
+ /* just copy it in-place and we're done */
+ values[nvalues] = newvalue;
+ return;
+ }
+
+ /*
+ * OK, I lied a bit - we won't check the smaller case explicitly, but
+ * we'll just compare the value to all existing values in the array.
+ * But we happen to start with the smallest value, so we're actually
+ * doing the check anyway.
+ *
+ * XXX We do walk the values sequentially. Perhaps we could/should be
+ * smarter and do some sort of bisection, to improve performance?
+ */
+ for (i = 0; i < nvalues; i++)
+ {
+ lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]);
+ if (DatumGetBool(lt))
+ {
+ /*
+ * Move values to make space for the new entry, which should go
+ * to index 'i'. Entries 0 ... (i-1) should stay where they are.
+ */
+ memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum));
+ values[i] = newvalue;
+ return;
+ }
+ }
+
+ /* We should never really get here. */
+ Assert(false);
+}
+
+/*
+ * Check that the order of the array values is correct, using the cmp
+ * function (which should be BTLessStrategyNumber).
+ */
+static void
+AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues)
+{
+#ifdef USE_ASSERT_CHECKING
+ int i;
+ Datum lt;
+
+ for (i = 0; i < (nvalues-1); i++)
+ {
+ lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]);
+ Assert(DatumGetBool(lt));
+ }
+#endif
+}
+
+/*
+ * Check ordering of boundary values in both parts of the ranges, using
+ * the cmp function (which should be BTLessStrategyNumber).
+ *
+ * XXX We might also check that the ranges and points do not overlap. But
+ * that will break this check sooner or later anyway.
+ */
+static void
+AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges)
+{
+#ifdef USE_ASSERT_CHECKING
+ /* first the ranges (there are 2*nranges boundary values) */
+ AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges);
+
+ /* then the single-point ranges (with nvalues boundar values ) */
+ AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges],
+ ranges->nvalues);
+#endif
+}
+
+/*
+ * Check that the expanded ranges (built when reducing the number of ranges
+ * by combining some of them) are correctly sorted and do not overlap.
+ */
+static void
+AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno,
+ Form_pg_attribute attr, CombineRange *ranges,
+ int nranges)
+{
+#ifdef USE_ASSERT_CHECKING
+ int i;
+ FmgrInfo *eq;
+ FmgrInfo *lt;
+
+ eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTEqualStrategyNumber);
+
+ lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /*
+ * Each range independently should be valid, i.e. that for the boundary
+ * values (lower <= upper).
+ */
+ for (i = 0; i < nranges; i++)
+ {
+ Datum r;
+ Datum minval = ranges[i].minval;
+ Datum maxval = ranges[i].maxval;
+
+ if (ranges[i].collapsed) /* collapsed: minval == maxval */
+ r = FunctionCall2Coll(eq, colloid, minval, maxval);
+ else /* non-collapsed: minval < maxval */
+ r = FunctionCall2Coll(lt, colloid, minval, maxval);
+
+ Assert(DatumGetBool(r));
+ }
+
+ /*
+ * And the ranges should be ordered and must nor overlap, i.e.
+ * upper < lower for boundaries of consecutive ranges.
+ */
+ for (i = 0; i < nranges-1; i++)
+ {
+ Datum r;
+ Datum maxval = ranges[i].maxval;
+ Datum minval = ranges[i+1].minval;
+
+ r = FunctionCall2Coll(lt, colloid, maxval, minval);
+
+ Assert(DatumGetBool(r));
+ }
+#endif
+}
+
+/*
+ * Expand ranges from Ranges into CombineRange array. This expects the
+ * cranges to be pre-allocated and sufficiently large (there needs to be
+ * at least (nranges + nvalues) slots).
+ */
+static void
+fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
+{
+ int idx;
+ int i;
+
+ idx = 0;
+ for (i = 0; i < ranges->nranges; i++)
+ {
+ cranges[idx].minval = ranges->values[2*i];
+ cranges[idx].maxval = ranges->values[2*i+1];
+ cranges[idx].collapsed = false;
+ idx++;
+
+ Assert(idx <= ncranges);
+ }
+
+ for (i = 0; i < ranges->nvalues; i++)
+ {
+ cranges[idx].minval = ranges->values[2*ranges->nranges + i];
+ cranges[idx].maxval = ranges->values[2*ranges->nranges + i];
+ cranges[idx].collapsed = true;
+ idx++;
+
+ Assert(idx <= ncranges);
+ }
+
+ Assert(idx == ncranges);
+
+ return;
+}
+
+/*
+ * Sort combine ranges using qsort (with BTLessStrategyNumber function).
+ */
+static void
+sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
+ CombineRange *cranges, int ncranges)
+{
+ compare_context cxt;
+
+ /* sort the values */
+ cxt.colloid = colloid;
+ cxt.cmpFn = cmp;
+
+ qsort_arg(cranges, ncranges, sizeof(CombineRange),
+ compare_combine_ranges, (void *) &cxt);
+}
+
+/*
+ * When combining multiple Range values (in union function), some of the
+ * ranges may overlap. We simply merge the overlapping ranges to fix that.
+ *
+ * XXX This assumes the combine ranges were previously sorted (by minval
+ * and then maxval). We leverage this when detecting overlap.
+ */
+static int
+merge_combine_ranges(FmgrInfo *cmp, Oid colloid,
+ CombineRange *cranges, int ncranges)
+{
+ int idx;
+
+ /* TODO: add assert checking the ordering of input ranges */
+
+ /* try merging ranges (idx) and (idx+1) if they overlap */
+ idx = 0;
+ while (idx < (ncranges-1))
+ {
+ Datum r;
+
+ /*
+ * comparing [?,maxval] vs. [minval,?] - the ranges overlap
+ * if (minval < maxval)
+ */
+ r = FunctionCall2Coll(cmp, colloid,
+ cranges[idx].maxval,
+ cranges[idx+1].minval);
+
+ /*
+ * Nope, maxval < minval, so no overlap. And we know the ranges
+ * are ordered, so there are no more overlaps, because all the
+ * remaining ranges have greater or equal minval.
+ */
+ if (DatumGetBool(r))
+ {
+ /* proceed to the next range */
+ idx += 1;
+ continue;
+ }
+
+ /*
+ * So ranges 'idx' and 'idx+1' do overlap, but we don't know if
+ * 'idx+1' is contained in 'idx', or if they only overlap only
+ * partially. So compare the upper bounds and keep the larger one.
+ */
+ r = FunctionCall2Coll(cmp, colloid,
+ cranges[idx].maxval,
+ cranges[idx+1].maxval);
+
+ if (DatumGetBool(r))
+ cranges[idx].maxval = cranges[idx+1].maxval;
+
+ /*
+ * The range certainly is no longer collapsed (irrespectedly of
+ * the previous state).
+ */
+ cranges[idx].collapsed = false;
+
+ /*
+ * Now get rid of the (idx+1) range entirely by shifting the
+ * remaining ranges by 1. There are ncranges elements, and we
+ * need to move elements from (idx+2). That means the number
+ * of elements to move is [ncranges - (idx+2)].
+ */
+ memmove(&cranges[idx+1], &cranges[idx+2],
+ (ncranges - (idx + 2)) * sizeof(CombineRange));
+
+ /*
+ * Decrease the number of ranges, and repeat (with the same range,
+ * as it might overlap with additional ranges thanks to the merge).
+ */
+ ncranges--;
+ }
+
+ /* TODO: add assert checking the ordering etc. of produced ranges */
+
+ return ncranges;
+}
+
+/*
+ * Represents a distance between two ranges (identified by index into
+ * an array of combine ranges).
+ */
+typedef struct DistanceValue
+{
+ int index;
+ double value;
+} DistanceValue;
+
+/*
+ * Simple comparator for distance values, comparing the double value.
+ */
+static int
+compare_distances(const void *a, const void *b)
+{
+ DistanceValue *da = (DistanceValue *)a;
+ DistanceValue *db = (DistanceValue *)b;
+
+ if (da->value < db->value)
+ return -1;
+ else if (da->value > db->value)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Given an array of combine ranges, compute distance of the gaps betwen
+ * the ranges - for ncranges there are (ncranges-1) gaps.
+ *
+ * We simply call the "distance" function to compute the (min-max) for pairs
+ * of consecutive ganges. The function may be fairly expensive, so we do that
+ * just once (and then use it to pick as many ranges to merge as possible).
+ *
+ * See reduce_combine_ranges for details.
+ */
+static DistanceValue *
+build_distances(FmgrInfo *distanceFn, Oid colloid,
+ CombineRange *cranges, int ncranges)
+{
+ int i;
+ DistanceValue *distances;
+
+ Assert(ncranges >= 2);
+
+ distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1));
+
+ /*
+ * Walk though the ranges once and compute distance between the ranges
+ * so that we can sort them once.
+ */
+ for (i = 0; i < (ncranges-1); i++)
+ {
+ Datum a1, a2, r;
+
+ a1 = cranges[i].maxval;
+ a2 = cranges[i+1].minval;
+
+ /* compute length of the empty gap (distance between max/min) */
+ r = FunctionCall2Coll(distanceFn, colloid, a1, a2);
+
+ /* remember the index */
+ distances[i].index = i;
+ distances[i].value = DatumGetFloat8(r);
+ }
+
+ /* sort the distances in ascending order */
+ pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances);
+
+ return distances;
+}
+
+/*
+ * Builds combine ranges for the existing ranges (and single-point ranges),
+ * and also the new value (which did not fit into the array).
+ *
+ * XXX We do perform qsort on all the values, but we could also leverage
+ * the fact that the input data is already sorted and do merge sort.
+ */
+static CombineRange *
+build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
+ Datum newvalue, int *nranges)
+{
+ int ncranges;
+ CombineRange *cranges;
+
+ /* now do the actual merge sort */
+ ncranges = ranges->nranges + ranges->nvalues + 1;
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
+ *nranges = ncranges;
+
+ /* put the new value at the beginning */
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
+
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+
+ /* and sort the ranges */
+ sort_combine_ranges(cmp, colloid, cranges, ncranges);
+
+ return cranges;
+}
+
+/*
+ * Counts bondary values needed to store the ranges. Each single-point
+ * range is stored using a single value, each regular range needs two.
+ */
+static int
+count_values(CombineRange *cranges, int ncranges)
+{
+ int i;
+ int count;
+
+ count = 0;
+ for (i = 0; i < ncranges; i++)
+ {
+ if (cranges[i].collapsed)
+ count += 1;
+ else
+ count += 2;
+ }
+
+ return count;
+}
+
+/*
+ * Combines ranges until the number of boundary values drops below 75%
+ * of the capacity (as set by values_per_range reloption).
+ *
+ * XXX The ranges to merge are selected solely using the distance. But
+ * that may not be the best strategy, for example when multiple gaps
+ * are of equal (or very similar) length.
+ *
+ * Consider for example points 1, 2, 3, .., 64, which have gaps of the
+ * same length 1 of course. In that case we tend to pick the first
+ * gap of that length, which leads to this:
+ *
+ * step 1: [1, 2], 3, 4, 5, .., 64
+ * step 2: [1, 3], 4, 5, .., 64
+ * step 3: [1, 4], 5, .., 64
+ * ...
+ *
+ * So in the end we'll have one "large" range and multiple small points.
+ * That may be fine, but it seems a bit strange and non-optimal. Maybe
+ * we should consider other things when picking ranges to merge - e.g.
+ * length of the ranges? Or perhaps randomize the choice of ranges, with
+ * probability inversely proportional to the distance (the gap lengths
+ * may be very close, but not exactly the same).
+ */
+static int
+reduce_combine_ranges(CombineRange *cranges, int ncranges,
+ DistanceValue *distances, int max_values)
+{
+ int i;
+ int ndistances = (ncranges - 1);
+ int count = count_values(cranges, ncranges);
+
+ /*
+ * We have one fewer 'gaps' than the ranges. We'll be decrementing
+ * the number of combine ranges (reduction is the primary goal of
+ * this function), so we must use a separate value.
+ */
+ for (i = 0; i < ndistances; i++)
+ {
+ int j;
+ int shortest;
+
+ if (count <= max_values * 0.75)
+ break;
+
+ shortest = distances[i].index;
+
+ /*
+ * The index must be still valid with respect to the current size
+ * of cranges array (and it always points to the first range, so
+ * never to the last one - hence the -1 in the condition).
+ */
+ Assert(shortest < (ncranges - 1));
+
+ if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed)
+ count -= 2;
+ else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed)
+ count -= 1;
+
+ /*
+ * Move the values to join the two selected ranges. The new range is
+ * definiely not collapsed but a regular range.
+ */
+ cranges[shortest].maxval = cranges[shortest+1].maxval;
+ cranges[shortest].collapsed = false;
+
+ /* shuffle the subsequent combine ranges */
+ memmove(&cranges[shortest+1], &cranges[shortest+2],
+ (ncranges - shortest - 2) * sizeof(CombineRange));
+
+ /* also, shuffle all higher indexes (we've just moved the ranges) */
+ for (j = i; j < ndistances; j++)
+ {
+ if (distances[j].index > shortest)
+ distances[j].index--;
+ }
+
+ ncranges--;
+
+ Assert(ncranges > 0);
+ }
+
+ return ncranges;
+}
+
+/*
+ * Store the boundary values from CombineRanges back into Range (using
+ * only the minimal number of values needed).
+ */
+static void
+store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges)
+{
+ int i;
+ int idx = 0;
+
+ /* first copy in the regular ranges */
+ ranges->nranges = 0;
+ for (i = 0; i < ncranges; i++)
+ {
+ if (!cranges[i].collapsed)
+ {
+ ranges->values[idx++] = cranges[i].minval;
+ ranges->values[idx++] = cranges[i].maxval;
+ ranges->nranges++;
+ }
+ }
+
+ /* now copy in the collapsed ones */
+ ranges->nvalues = 0;
+ for (i = 0; i < ncranges; i++)
+ {
+ if (cranges[i].collapsed)
+ {
+ ranges->values[idx++] = cranges[i].minval;
+ ranges->nvalues++;
+ }
+ }
+}
+
+/*
+ * range_add_value
+ * Add the new value to the multi-minmax range.
+ */
+static bool
+range_add_value(BrinDesc *bdesc, Oid colloid,
+ AttrNumber attno, Form_pg_attribute attr,
+ Ranges *ranges, Datum newval)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+
+ /*
+ * Bail out if the value already is covered by the range.
+ *
+ * We could also add values until we hit values_per_range, and then
+ * do the deduplication in a batch, hoping for better efficiency. But
+ * that would mean we actually modify the range every time, which means
+ * having to serialize the value, which does palloc, walks the values,
+ * copies them, etc. Not exactly cheap.
+ *
+ * So instead we do the check, which should be fairly cheap - assuming
+ * the comparator function is not very expensive.
+ *
+ * This also implies means the values array can't contain duplicities.
+ */
+ if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval))
+ return false;
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /*
+ * If there's space in the values array, copy it in and we're done.
+ *
+ * We do want to keep the values sorted (to speed up searches), so we
+ * do a simple insertion sort. We could do something more elaborate,
+ * e.g. by sorting the values only now and then, but for small counts
+ * (e.g. when maxvalues is 64) this should be fine.
+ */
+ if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues)
+ {
+ Datum *values;
+
+ /* beginning of the 'single value' part (for convenience) */
+ values = &ranges->values[2*ranges->nranges];
+
+ insert_value(cmpFn, colloid, values, ranges->nvalues, newval);
+
+ ranges->nvalues++;
+
+ /*
+ * Check we haven't broken the ordering of boundary values (checks
+ * both parts, but that doesn't hurt).
+ */
+ AssertRangeOrdering(cmpFn, colloid, ranges);
+
+ /* yep, we've modified the range */
+ return true;
+ }
+
+ /*
+ * Damn - the new value is not in the range yet, but we don't have space
+ * to just insert it. So we need to combine some of the existing ranges,
+ * to reduce the number of values we need to store (joining two intervals
+ * reduces the number of boundaries to store by 2).
+ *
+ * To do that we first construct an array of CombineRange items - each
+ * combine range tracks if it's a regular range or collapsed range, where
+ * "collapsed" means "single point."
+ *
+ * Existing ranges (we have ranges->nranges of them) map to combine ranges
+ * directly, while single points (ranges->nvalues of them) have to be
+ * expanded. We neet the combine ranges to be sorted, and we do that by
+ * performing a merge sort of ranges, values and new value.
+ */
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we release at least 25% of the space. This
+ * threshold is somewhat arbitrary, perhaps needs tuning. We must not
+ * use too low or high value.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ ranges->maxvalues);
+
+ Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+
+ return true;
+}
+
+Datum
+brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
+{
+ BrinOpcInfo *result;
+
+ /*
+ * opaque->strategy_procinfos is initialized lazily; here it is set to
+ * all-uninitialized by palloc0 which sets fn_oid to InvalidOid.
+ */
+
+ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) +
+ sizeof(MinmaxMultiOpaque));
+ result->oi_nstored = 1;
+ result->oi_regular_nulls = true;
+ result->oi_opaque = (MinmaxMultiOpaque *)
+ MAXALIGN((char *) result + SizeofBrinOpcInfo(1));
+ result->oi_typcache[0] = lookup_type_cache(BRINMINMAXMULTISUMMARYOID, 0);
+
+ PG_RETURN_POINTER(result);
+}
+
+/*
+ * Compute distance between two float4 values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS)
+{
+ float a1 = PG_GETARG_FLOAT4(0);
+ float a2 = PG_GETARG_FLOAT4(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(a1 <= a2);
+
+ PG_RETURN_FLOAT8((double) a2 - (double) a1);
+}
+
+/*
+ * Compute distance between two float8 values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS)
+{
+ double a1 = PG_GETARG_FLOAT8(0);
+ double a2 = PG_GETARG_FLOAT8(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(a1 <= a2);
+
+ PG_RETURN_FLOAT8(a2 - a1);
+}
+
+/*
+ * Compute distance between two int2 values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS)
+{
+ int16 a1 = PG_GETARG_INT16(0);
+ int16 a2 = PG_GETARG_INT16(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(a1 <= a2);
+
+ PG_RETURN_FLOAT8((double) a2 - (double) a1);
+}
+
+/*
+ * Compute distance between two int4 values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS)
+{
+ int32 a1 = PG_GETARG_INT32(0);
+ int32 a2 = PG_GETARG_INT32(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(a1 <= a2);
+
+ PG_RETURN_FLOAT8((double) a2 - (double) a1);
+}
+
+/*
+ * Compute distance between two int8 values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS)
+{
+ int64 a1 = PG_GETARG_INT64(0);
+ int64 a2 = PG_GETARG_INT64(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(a1 <= a2);
+
+ PG_RETURN_FLOAT8((double) a2 - (double) a1);
+}
+
+/*
+ * Compute distance between two tid values (by mapping them to float8
+ * and then subtracting them).
+ */
+Datum
+brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS)
+{
+ double da1,
+ da2;
+
+ ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0);
+ ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(ItemPointerCompare(pa1, pa2) <= 0);
+
+ da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage +
+ ItemPointerGetOffsetNumber(pa1);
+
+ da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage +
+ ItemPointerGetOffsetNumber(pa2);
+
+ PG_RETURN_FLOAT8(da2 - da1);
+}
+
+/*
+ * Comutes distance between two numeric values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS)
+{
+ Datum d;
+ Datum a1 = PG_GETARG_DATUM(0);
+ Datum a2 = PG_GETARG_DATUM(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2)));
+
+ d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */
+
+ PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d));
+}
+
+/*
+ * Computes approximate distance between two UUID values.
+ *
+ * XXX We do not need a perfectly accurate value, so we approximate the
+ * deltas (which would have to be 128-bit integers) with a 64-bit float.
+ * The small inaccuracies do not matter in practice, in the worst case
+ * we'll decide to merge ranges that are not the closest ones.
+ */
+Datum
+brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS)
+{
+ int i;
+ double delta = 0;
+
+ Datum a1 = PG_GETARG_DATUM(0);
+ Datum a2 = PG_GETARG_DATUM(1);
+
+ pg_uuid_t *u1 = DatumGetUUIDP(a1);
+ pg_uuid_t *u2 = DatumGetUUIDP(a2);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2)));
+
+ /* compute approximate delta as a double precision value */
+ for (i = UUID_LEN-1; i >= 0; i--)
+ {
+ delta += (int) u2->data[i] - (int) u1->data[i];
+ delta /= 256;
+ }
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Computes approximate distance between two time (without tz) values.
+ *
+ * TimeADT is just an int64, so we simply subtract the values directly.
+ */
+Datum
+brin_minmax_multi_distance_time(PG_FUNCTION_ARGS)
+{
+ double delta = 0;
+
+ TimeADT ta = PG_GETARG_TIMEADT(0);
+ TimeADT tb = PG_GETARG_TIMEADT(1);
+
+ delta = (tb - ta);
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Computes approximate distance between two timetz values.
+ *
+ * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT.
+ *
+ * XXX Does this need to consider the time zones?
+ */
+Datum
+brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS)
+{
+ double delta = 0;
+
+ TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0);
+ TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1);
+
+ delta = tb->time - ta->time;
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Computes distance between two interval values.
+ *
+ * Intervals are internally just 'time' values, so use the same approach
+ * as for in brin_minmax_multi_distance_time.
+ *
+ * XXX Do we actually need two separate functions, then?
+ */
+Datum
+brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS)
+{
+ double delta = 0;
+
+ TimeADT ia = PG_GETARG_TIMEADT(0);
+ TimeADT ib = PG_GETARG_TIMEADT(1);
+
+ delta = (ib - ia);
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Compute distance between two pg_lsn values.
+ *
+ * LSN is just an int64 encoding position in the stream, so just subtract
+ * those int64 values directly.
+ */
+Datum
+brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS)
+{
+ double delta = 0;
+
+ XLogRecPtr lsna = PG_GETARG_LSN(0);
+ XLogRecPtr lsnb = PG_GETARG_LSN(1);
+
+ delta = (lsnb - lsna);
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Compute distance between two macaddr values.
+ *
+ * mac addresses are treated as 6 unsigned chars, so do the same thing we
+ * already do for UUID values.
+ */
+Datum
+brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS)
+{
+ double delta;
+
+ macaddr *a = PG_GETARG_MACADDR_P(0);
+ macaddr *b = PG_GETARG_MACADDR_P(1);
+
+ delta = ((double)b->f - (double)a->f);
+ delta /= 256;
+
+ delta += ((double)b->e - (double)a->e);
+ delta /= 256;
+
+ delta += ((double)b->d - (double)a->d);
+ delta /= 256;
+
+ delta += ((double)b->c - (double)a->c);
+ delta /= 256;
+
+ delta += ((double)b->b - (double)a->b);
+ delta /= 256;
+
+ delta += ((double)b->a - (double)a->a);
+ delta /= 256;
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Compute distance between two macaddr8 values.
+ *
+ * macaddr8 addresses are 8 unsigned chars, so do the same thing we
+ * already do for UUID values.
+ */
+Datum
+brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS)
+{
+ double delta;
+
+ macaddr8 *a = PG_GETARG_MACADDR8_P(0);
+ macaddr8 *b = PG_GETARG_MACADDR8_P(1);
+
+ delta = ((double)b->h - (double)a->h);
+ delta /= 256;
+
+ delta += ((double)b->g - (double)a->g);
+ delta /= 256;
+
+ delta += ((double)b->f - (double)a->f);
+ delta /= 256;
+
+ delta += ((double)b->e - (double)a->e);
+ delta /= 256;
+
+ delta += ((double)b->d - (double)a->d);
+ delta /= 256;
+
+ delta += ((double)b->c - (double)a->c);
+ delta /= 256;
+
+ delta += ((double)b->b - (double)a->b);
+ delta /= 256;
+
+ delta += ((double)b->a - (double)a->a);
+ delta /= 256;
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Compute distance between two inet values.
+ *
+ * The distance is defined as difference between 32-bit/128-bit values,
+ * depending on the IP version. The distance is computed by subtracting
+ * the bytes and normalizing it to [0,1] range for each IP family.
+ * Addresses from difference families are consider to be in maximum
+ * distance, which is 1.0.
+ *
+ * XXX Does this need to consider the mask (bits)? For now it's ignored.
+ */
+Datum
+brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
+{
+ double delta;
+ int i;
+ int len;
+ unsigned char *addra,
+ *addrb;
+
+ inet *ipa = PG_GETARG_INET_PP(0);
+ inet *ipb = PG_GETARG_INET_PP(1);
+
+ /*
+ * If the addresses are from different families, consider them to be
+ * in maximal possible distance (which is 1.0).
+ */
+ if (ip_family(ipa) != ip_family(ipb))
+ return 1.0;
+
+ /* ipv4 or ipv6 */
+ if (ip_family(ipa) == PGSQL_AF_INET)
+ len = 4;
+ else
+ len = 16; /* NS_IN6ADDRSZ */
+
+ addra = ip_addr(ipa);
+ addrb = ip_addr(ipb);
+
+ delta = 0;
+ for (i = len-1; i >= 0; i--)
+ {
+ delta += (double)addrb[i] - (double)addra[i];
+ delta /= 256;
+ }
+
+ Assert((delta >= 0) && (delta <= 1));
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+static void
+brin_minmax_multi_serialize(Datum src, Datum *dst)
+{
+ Ranges *ranges = (Ranges *) DatumGetPointer(src);
+ SerializedRanges *s = range_serialize(ranges);
+ dst[0] = PointerGetDatum(s);
+}
+
+static int
+brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts)
+{
+ return MinMaxGetValuesPerRange(opts);
+}
+
+/*
+ * Examine the given index tuple (which contains partial status of a certain
+ * page range) by comparing it to the given value that comes from another heap
+ * tuple. If the new value is outside the min/max range specified by the
+ * existing tuple values, update the index tuple and return true. Otherwise,
+ * return false and do not modify in this case.
+ */
+Datum
+brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
+{
+ BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+ BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
+ Datum newval = PG_GETARG_DATUM(2);
+ bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3);
+ MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION();
+ bool modified = false;
+ Form_pg_attribute attr;
+ AttrNumber attno;
+ Ranges *ranges;
+ SerializedRanges *serialized = NULL;
+
+ Assert(!isnull);
+
+ attno = column->bv_attno;
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+
+ /* use the already deserialized value, is possible */
+ ranges = (Ranges *) DatumGetPointer(column->bv_mem_value);
+
+ /*
+ * If this is the first non-null value, we need to initialize the range
+ * list. Otherwise just extract the existing range list from BrinValues.
+ */
+ if (column->bv_allnulls)
+ {
+ MemoryContext oldctx;
+
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+
+ ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ ranges->typid = attr->atttypid;
+
+ MemoryContextSwitchTo(oldctx);
+
+ column->bv_allnulls = false;
+ modified = true;
+
+ column->bv_mem_value = PointerGetDatum(ranges);
+ column->bv_serialize = brin_minmax_multi_serialize;
+ }
+ else if (!ranges)
+ {
+ MemoryContext oldctx;
+
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+
+ serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+ ranges = range_deserialize(serialized);
+
+ column->bv_mem_value = PointerGetDatum(ranges);
+ column->bv_serialize = brin_minmax_multi_serialize;
+
+ MemoryContextSwitchTo(oldctx);
+ }
+
+ /*
+ * Try to add the new value to the range. We need to update the modified
+ * flag, so that we serialize the correct value.
+ */
+ modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval);
+
+
+ PG_RETURN_BOOL(modified);
+}
+
+/*
+ * 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.
+ */
+Datum
+brin_minmax_multi_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);
+ // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION(),
+ subtype;
+ AttrNumber attno;
+ Datum value;
+ FmgrInfo *finfo;
+ SerializedRanges *serialized;
+ Ranges *ranges;
+ int keyno;
+ int rangeno;
+ int i;
+
+ attno = column->bv_attno;
+
+ serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+ ranges = range_deserialize(serialized);
+
+ /* inspect the ranges, and for each one evaluate the scan keys */
+ for (rangeno = 0; rangeno < ranges->nranges; rangeno++)
+ {
+ Datum minval = ranges->values[2*rangeno];
+ Datum maxval = ranges->values[2*rangeno+1];
+
+ /* assume the range is matching, and we'll try to prove otherwise */
+ bool matching = true;
+
+ for (keyno = 0; keyno < nkeys; keyno++)
+ {
+ Datum matches;
+ ScanKey key = keys[keyno];
+
+ /* NULL keys are handled and filtered-out in bringetbitmap */
+ Assert(!(key->sk_flags & SK_ISNULL));
+
+ attno = key->sk_attno;
+ subtype = key->sk_subtype;
+ value = key->sk_argument;
+ switch (key->sk_strategy)
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ key->sk_strategy);
+ /* first value from the array */
+ matches = FunctionCall2Coll(finfo, colloid, minval, value);
+ break;
+
+ case BTEqualStrategyNumber:
+ {
+ Datum compar;
+ FmgrInfo *cmpFn;
+
+ /* by default this range does not match */
+ matches = false;
+
+ /*
+ * Otherwise, need to compare the new value with boundaries of all
+ * the ranges. First check if it's less than the absolute minimum,
+ * which is the first value in the array.
+ */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ BTLessStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, colloid, value, minval);
+
+ /* smaller than the smallest value in this range */
+ if (DatumGetBool(compar))
+ break;
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ BTGreaterStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, colloid, value, maxval);
+
+ /* larger than the largest value in this range */
+ if (DatumGetBool(compar))
+ break;
+
+ /* haven't managed to eliminate this range, so consider it matching */
+ matches = true;
+
+ break;
+ }
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ key->sk_strategy);
+ /* last value from the array */
+ matches = FunctionCall2Coll(finfo, colloid, maxval, value);
+ break;
+
+ default:
+ /* shouldn't happen */
+ elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+ matches = 0;
+ break;
+ }
+
+ /* the range has to match all the scan keys */
+ matching &= DatumGetBool(matches);
+
+ /* once we find a non-matching key, we're done */
+ if (! matching)
+ break;
+ }
+
+ /* have we found a range matching all scan keys? if yes, we're
+ * done */
+ if (matching)
+ PG_RETURN_DATUM(BoolGetDatum(true));
+ }
+
+ /* and now inspect the values */
+ for (i = 0; i < ranges->nvalues; i++)
+ {
+ Datum val = ranges->values[2*ranges->nranges + i];
+
+ /* assume the range is matching, and we'll try to prove otherwise */
+ bool matching = true;
+
+ for (keyno = 0; keyno < nkeys; keyno++)
+ {
+ Datum matches;
+ ScanKey key = keys[keyno];
+
+ /* we've already dealt with NULL keys at the beginning */
+ if (key->sk_flags & SK_ISNULL)
+ continue;
+
+ attno = key->sk_attno;
+ subtype = key->sk_subtype;
+ value = key->sk_argument;
+ switch (key->sk_strategy)
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ case BTEqualStrategyNumber:
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+
+ finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ key->sk_strategy);
+ matches = FunctionCall2Coll(finfo, colloid, val, value);
+ break;
+
+ default:
+ /* shouldn't happen */
+ elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+ matches = 0;
+ break;
+ }
+
+ /* the range has to match all the scan keys */
+ matching &= DatumGetBool(matches);
+
+ /* once we find a non-matching key, we're done */
+ if (! matching)
+ break;
+ }
+
+ /* have we found a range matching all scan keys? if yes, we're
+ * done */
+ if (matching)
+ PG_RETURN_DATUM(BoolGetDatum(true));
+ }
+
+ PG_RETURN_DATUM(BoolGetDatum(false));
+}
+
+/*
+ * Given two BrinValues, update the first of them as a union of the summary
+ * values contained in both. The second one is untouched.
+ */
+Datum
+brin_minmax_multi_union(PG_FUNCTION_ARGS)
+{
+ BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+ BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
+ BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
+ // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION();
+ SerializedRanges *serialized_a;
+ SerializedRanges *serialized_b;
+ Ranges *ranges_a;
+ Ranges *ranges_b;
+ AttrNumber attno;
+ Form_pg_attribute attr;
+ CombineRange *cranges;
+ int ncranges;
+ FmgrInfo *cmpFn,
+ *distanceFn;
+ DistanceValue *distances;
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ Assert(col_a->bv_attno == col_b->bv_attno);
+ Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
+
+ attno = col_a->bv_attno;
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+
+ serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]);
+ serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]);
+
+ ranges_a = range_deserialize(serialized_a);
+ ranges_b = range_deserialize(serialized_b);
+
+ /* make sure neither of the ranges is NULL */
+ Assert(ranges_a && ranges_b);
+
+ ncranges = (ranges_a->nranges + ranges_a->nvalues) +
+ (ranges_b->nranges + ranges_b->nvalues);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* allocate and fill */
+ cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+
+ /* fill the combine ranges with entries for the first range */
+ fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
+ ranges_a);
+
+ /* and now add combine ranges for the second range */
+ fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues],
+ ranges_b->nranges + ranges_b->nvalues,
+ ranges_b);
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /* sort the combine ranges */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+
+ /*
+ * We've merged two different lists of ranges, so some of them may be
+ * overlapping. So walk through them and merge them.
+ */
+ ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges);
+
+ /* check that the combine ranges are correct (no overlaps, ordering) */
+ AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges);
+
+ /* build array of gap distances and sort them in ascending order */
+ distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE);
+ distances = build_distances(distanceFn, colloid, cranges, ncranges);
+
+ /*
+ * See how many values would be needed to store the current ranges, and if
+ * needed combine as many off them to get below the maxvalues threshold.
+ * The collapsed ranges will be stored as a single value.
+ *
+ * XXX The maxvalues may be different, so perhaps use Max?
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ ranges_a->maxvalues);
+
+ /* update the first range summary */
+ store_combine_ranges(ranges_a, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* cleanup and update the serialized value */
+ pfree(serialized_a);
+ col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a));
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Cache and return minmax multi opclass support procedure
+ *
+ * Return the procedure corresponding to the given function support number
+ * or null if it is not exists.
+ */
+static FmgrInfo *
+minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
+{
+ MinmaxMultiOpaque *opaque;
+ uint16 basenum = procnum - PROCNUM_BASE;
+
+ /*
+ * We cache these in the opaque struct, to avoid repetitive syscache
+ * lookups.
+ */
+ opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+ /*
+ * If we already searched for this proc and didn't find it, don't bother
+ * searching again.
+ */
+ if (opaque->extra_proc_missing[basenum])
+ return NULL;
+
+ if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
+ {
+ if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
+ procnum)))
+ {
+ fmgr_info_copy(&opaque->extra_procinfos[basenum],
+ index_getprocinfo(bdesc->bd_index, attno, procnum),
+ bdesc->bd_context);
+ }
+ else
+ {
+ opaque->extra_proc_missing[basenum] = true;
+ return NULL;
+ }
+ }
+
+ return &opaque->extra_procinfos[basenum];
+}
+
+/*
+ * Cache and return the procedure for the given strategy.
+ *
+ * Note: this function mirrors minmax_multi_get_strategy_procinfo; see notes
+ * there. If changes are made here, see that function too.
+ */
+static FmgrInfo *
+minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
+ uint16 strategynum)
+{
+ MinmaxMultiOpaque *opaque;
+
+ Assert(strategynum >= 1 &&
+ strategynum <= BTMaxStrategyNumber);
+
+ opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+ /*
+ * We cache the procedures for the previous subtype in the opaque struct,
+ * to avoid repetitive syscache lookups. If the subtype changed,
+ * invalidate all the cached entries.
+ */
+ if (opaque->cached_subtype != subtype)
+ {
+ uint16 i;
+
+ for (i = 1; i <= BTMaxStrategyNumber; i++)
+ opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
+ opaque->cached_subtype = subtype;
+ }
+
+ if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
+ {
+ Form_pg_attribute attr;
+ HeapTuple tuple;
+ Oid opfamily,
+ oprid;
+ bool isNull;
+
+ opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+ tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(attr->atttypid),
+ ObjectIdGetDatum(subtype),
+ Int16GetDatum(strategynum));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
+ strategynum, attr->atttypid, subtype, opfamily);
+
+ oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple,
+ Anum_pg_amop_amopopr, &isNull));
+ ReleaseSysCache(tuple);
+ Assert(!isNull && RegProcedureIsValid(oprid));
+
+ fmgr_info_cxt(get_opcode(oprid),
+ &opaque->strategy_procinfos[strategynum - 1],
+ bdesc->bd_context);
+ }
+
+ return &opaque->strategy_procinfos[strategynum - 1];
+}
+
+Datum
+brin_minmax_multi_options(PG_FUNCTION_ARGS)
+{
+ local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+ init_local_reloptions(relopts, sizeof(MinMaxOptions));
+
+ add_local_int_reloption(relopts, "values_per_range", "desc",
+ 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange));
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * brin_minmax_multi_summary_in
+ * - input routine for type brin_minmax_multi_summary.
+ *
+ * brin_minmax_multi_summary is only used internally to represent summaries
+ * in BRIN minmax-multi indexes, so it has no operations of its own, and we
+ * disallow input too.
+ */
+Datum
+brin_minmax_multi_summary_in(PG_FUNCTION_ARGS)
+{
+ /*
+ * brin_minmax_multi_summary stores the data in binary form and parsing
+ * text input is not needed, so disallow this.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "brin_minmax_multi_summary")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+
+/*
+ * brin_minmax_multi_summary_out
+ * - output routine for type brin_minmax_multi_summary.
+ *
+ * BRIN minmax-multi summaries are serialized into a bytea value, but we
+ * want to output something nicer humans can understand.
+ */
+Datum
+brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
+{
+ int i;
+ int idx;
+ SerializedRanges *ranges;
+ Ranges *ranges_deserialized;
+ StringInfoData str;
+ bool isvarlena;
+ Oid outfunc;
+ FmgrInfo fmgrinfo;
+ ArrayBuildState *astate_values = NULL;
+
+ initStringInfo(&str);
+ appendStringInfoChar(&str, '{');
+
+ /*
+ * XXX not sure the detoasting is necessary (probably not, this
+ * can only be in an index).
+ */
+ ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+
+ /* lookup output func for the type */
+ getTypeOutputInfo(ranges->typid, &outfunc, &isvarlena);
+ fmgr_info(outfunc, &fmgrinfo);
+
+ /* deserialize the range info easy-to-process pieces */
+ ranges_deserialized = range_deserialize(ranges);
+
+ appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u",
+ ranges_deserialized->nranges,
+ ranges_deserialized->nvalues,
+ ranges_deserialized->maxvalues);
+
+ /* serialize ranges */
+ idx = 0;
+ for (i = 0; i < ranges_deserialized->nranges; i++)
+ {
+ Datum a, b;
+ text *c;
+ StringInfoData str;
+
+ initStringInfo(&str);
+
+ a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]);
+ b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]);
+
+ appendStringInfo(&str, "%s ... %s",
+ DatumGetPointer(a),
+ DatumGetPointer(b));
+
+ c = cstring_to_text(str.data);
+
+ astate_values = accumArrayResult(astate_values,
+ PointerGetDatum(c),
+ false,
+ TEXTOID,
+ CurrentMemoryContext);
+ }
+
+ if (ranges_deserialized->nranges > 0)
+ {
+ Oid typoutput;
+ bool typIsVarlena;
+ Datum val;
+ char *extval;
+
+ getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
+
+ val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+
+ extval = OidOutputFunctionCall(typoutput, val);
+
+ appendStringInfo(&str, " ranges: %s", extval);
+ }
+
+ /* serialize individual values */
+ astate_values = NULL;
+
+ for (i = 0; i < ranges_deserialized->nvalues; i++)
+ {
+ Datum a;
+ text *b;
+ StringInfoData str;
+
+ initStringInfo(&str);
+
+ a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]);
+
+ appendStringInfo(&str, "%s", DatumGetPointer(a));
+
+ b = cstring_to_text(str.data);
+
+ astate_values = accumArrayResult(astate_values,
+ PointerGetDatum(b),
+ false,
+ TEXTOID,
+ CurrentMemoryContext);
+ }
+
+ if (ranges_deserialized->nvalues > 0)
+ {
+ Oid typoutput;
+ bool typIsVarlena;
+ Datum val;
+ char *extval;
+
+ getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
+
+ val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+
+ extval = OidOutputFunctionCall(typoutput, val);
+
+ appendStringInfo(&str, " values: %s", extval);
+ }
+
+
+ appendStringInfoChar(&str, '}');
+
+ PG_RETURN_CSTRING(str.data);
+}
+
+/*
+ * brin_minmax_multi_summary_recv
+ * - binary input routine for type brin_minmax_multi_summary.
+ */
+Datum
+brin_minmax_multi_summary_recv(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "brin_minmax_multi_summary")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * brin_minmax_multi_summary_send
+ * - binary output routine for type brin_minmax_multi_summary.
+ *
+ * BRIN minmax-multi summaries are serialized in a bytea value (although
+ * the type is named differently), so let's just send that.
+ */
+Datum
+brin_minmax_multi_summary_send(PG_FUNCTION_ARGS)
+{
+ return byteasend(fcinfo);
+}
+
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index 46e6b23c87..8bdf837070 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -138,6 +138,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_hasnulls)
anynulls = true;
+ /* if needed, serialize the values before forming the on-disk tuple */
+ if (tuple->bt_columns[keyno].bv_serialize)
+ {
+ tuple->bt_columns[keyno].bv_serialize(
+ tuple->bt_columns[keyno].bv_mem_value,
+ tuple->bt_columns[keyno].bv_values);
+ }
+
for (datumno = 0;
datumno < brdesc->bd_info[keyno]->oi_nstored;
datumno++)
@@ -398,6 +406,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc)
dtuple->bt_columns[i].bv_allnulls = true;
dtuple->bt_columns[i].bv_hasnulls = false;
dtuple->bt_columns[i].bv_values = (Datum *) currdatum;
+
+ dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL);
+ dtuple->bt_columns[i].bv_serialize = NULL;
+ dtuple->bt_columns[i].bv_context = dtuple->bt_context;
+
currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored;
}
@@ -477,6 +490,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple)
dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno];
dtup->bt_columns[keyno].bv_allnulls = false;
+
+ dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL);
+ dtup->bt_columns[keyno].bv_serialize = NULL;
+ dtup->bt_columns[keyno].bv_context = dtup->bt_context;
}
MemoryContextSwitchTo(oldcxt);
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index b02298c0aa..03499281c6 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -24,6 +24,7 @@ typedef struct BrinOptions
bool autosummarize;
double nDistinctPerRange; /* number of distinct values per range */
double falsePositiveRate; /* false positive for bloom filter */
+ int valuesPerRange; /* number of values per range */
} BrinOptions;
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index e3c9d47503..ee4d0706df 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -62,6 +62,9 @@ typedef struct BrinDesc
double bd_nDistinctPerRange;
double bd_falsePositiveRange;
+ /* parameters for multi-minmax indexes */
+ int bd_valuesPerRange;
+
/* per-column info; bd_tupdesc->natts entries long */
BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER];
} BrinDesc;
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index a9ccc3995b..064a93d09b 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,6 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
+typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
@@ -27,6 +28,9 @@ typedef struct BrinValues
bool bv_hasnulls; /* are there any nulls in the page range? */
bool bv_allnulls; /* are all values nulls in the page range? */
Datum *bv_values; /* current accumulated values */
+ Datum bv_mem_value; /* expanded accumulated values */
+ MemoryContext bv_context;
+ brin_serialize_callback_type bv_serialize;
} BrinValues;
/*
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 2f1f144db4..d3d12a7b99 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest)
* ----------
*/
#define FirstGenbkiObjectId 10000
-#define FirstBootstrapObjectId 12000
+#define FirstBootstrapObjectId 13000
#define FirstNormalObjectId 16384
/*
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 12033d48ec..cda1ad4fbe 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1900,6 +1900,152 @@
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
amopmethod => 'brin' },
+# minmax multi integer
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
+ amopmethod => 'brin' },
+
# bloom integer
{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8',
@@ -1977,6 +2123,23 @@
amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)',
amopmethod => 'brin' },
+# minmax multi oid
+{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)',
+ amopmethod => 'brin' },
+
# bloom oid
{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid',
amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)',
@@ -2003,6 +2166,22 @@
{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid',
amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)',
amopmethod => 'brin' },
+# minmax multi tid
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)',
+ amopmethod => 'brin' },
# minmax float (float4, float8)
@@ -2070,6 +2249,72 @@
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
amopmethod => 'brin' },
+# minmax multi float (float4, float8)
+
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '2',
+ amopopr => '<=(float4,float4)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '4',
+ amopopr => '>=(float4,float4)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '2',
+ amopopr => '<=(float4,float8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '4',
+ amopopr => '>=(float4,float8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '2',
+ amopopr => '<=(float8,float4)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '4',
+ amopopr => '>=(float8,float4)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '2',
+ amopopr => '<=(float8,float8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '4',
+ amopopr => '>=(float8,float8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
+ amopmethod => 'brin' },
+
# bloom float
{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4',
amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)',
@@ -2101,6 +2346,23 @@
amoprighttype => 'macaddr', amopstrategy => '5',
amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' },
+# minmax multi macaddr
+{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '1',
+ amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '2',
+ amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '3',
+ amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '4',
+ amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '5',
+ amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' },
+
# bloom macaddr
{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr',
amoprighttype => 'macaddr', amopstrategy => '1',
@@ -2123,6 +2385,23 @@
amoprighttype => 'macaddr8', amopstrategy => '5',
amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' },
+# minmax multi macaddr8
+{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '1',
+ amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '2',
+ amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '3',
+ amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '4',
+ amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '5',
+ amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' },
+
# bloom macaddr8
{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8',
amoprighttype => 'macaddr8', amopstrategy => '1',
@@ -2145,6 +2424,23 @@
amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)',
amopmethod => 'brin' },
+# minmax multi inet
+{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)',
+ amopmethod => 'brin' },
+
# bloom inet
{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet',
amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)',
@@ -2209,6 +2505,23 @@
amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
amopmethod => 'brin' },
+# minmax multi time without time zone
+{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
+ amopmethod => 'brin' },
+
# bloom time without time zone
{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time',
amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)',
@@ -2360,6 +2673,152 @@
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' },
+# minmax multi datetime (date, timestamp, timestamptz)
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '2',
+ amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '3',
+ amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '4',
+ amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '5',
+ amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '2',
+ amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '3',
+ amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '4',
+ amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '5',
+ amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '<(date,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '2',
+ amopopr => '<=(date,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '3',
+ amopopr => '=(date,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '4',
+ amopopr => '>=(date,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '5',
+ amopopr => '>(date,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '<(date,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '2',
+ amopopr => '<=(date,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '3',
+ amopopr => '=(date,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '4',
+ amopopr => '>=(date,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '5',
+ amopopr => '>(date,timestamptz)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '1',
+ amopopr => '<(timestamptz,date)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '2',
+ amopopr => '<=(timestamptz,date)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '3',
+ amopopr => '=(timestamptz,date)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '4',
+ amopopr => '>=(timestamptz,date)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '5',
+ amopopr => '>(timestamptz,date)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '2',
+ amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '3',
+ amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '4',
+ amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '5',
+ amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '2',
+ amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '3',
+ amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '4',
+ amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '5',
+ amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' },
+
# bloom datetime (date, timestamp, timestamptz)
{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp',
@@ -2415,6 +2874,23 @@
amoprighttype => 'interval', amopstrategy => '5',
amopopr => '>(interval,interval)', amopmethod => 'brin' },
+# minmax multi interval
+{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '1',
+ amopopr => '<(interval,interval)', amopmethod => 'brin' },
+{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '2',
+ amopopr => '<=(interval,interval)', amopmethod => 'brin' },
+{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '3',
+ amopopr => '=(interval,interval)', amopmethod => 'brin' },
+{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '4',
+ amopopr => '>=(interval,interval)', amopmethod => 'brin' },
+{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '5',
+ amopopr => '>(interval,interval)', amopmethod => 'brin' },
+
# bloom interval
{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval',
amoprighttype => 'interval', amopstrategy => '1',
@@ -2437,6 +2913,23 @@
amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)',
amopmethod => 'brin' },
+# minmax multi time with time zone
+{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '2',
+ amopopr => '<=(timetz,timetz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '4',
+ amopopr => '>=(timetz,timetz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)',
+ amopmethod => 'brin' },
+
# bloom time with time zone
{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz',
amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)',
@@ -2493,6 +2986,23 @@
amoprighttype => 'numeric', amopstrategy => '5',
amopopr => '>(numeric,numeric)', amopmethod => 'brin' },
+# minmax multi numeric
+{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '1',
+ amopopr => '<(numeric,numeric)', amopmethod => 'brin' },
+{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '2',
+ amopopr => '<=(numeric,numeric)', amopmethod => 'brin' },
+{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '3',
+ amopopr => '=(numeric,numeric)', amopmethod => 'brin' },
+{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '4',
+ amopopr => '>=(numeric,numeric)', amopmethod => 'brin' },
+{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '5',
+ amopopr => '>(numeric,numeric)', amopmethod => 'brin' },
+
# bloom numeric
{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric',
amoprighttype => 'numeric', amopstrategy => '1',
@@ -2515,6 +3025,23 @@
amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)',
amopmethod => 'brin' },
+# minmax multi uuid
+{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)',
+ amopmethod => 'brin' },
+
# bloom uuid
{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid',
amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)',
@@ -2581,6 +3108,23 @@
amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)',
amopmethod => 'brin' },
+# minmax multi pg_lsn
+{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '2',
+ amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' },
+{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '4',
+ amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' },
+{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)',
+ amopmethod => 'brin' },
+
# bloom pg_lsn
{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn',
amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 4e35bb3459..082fbe10b5 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -951,6 +951,152 @@
{ amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4',
amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' },
+# minmax multi integer: int2, int4, int8
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' },
+
# bloom integer: int2, int4, int8
{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
amprocrighttype => 'int8', amprocnum => '1',
@@ -1046,6 +1192,23 @@
{ amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid',
amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' },
+# minmax multi oid
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' },
+
# bloom oid
{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' },
@@ -1092,6 +1255,23 @@
{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' },
+# minmax multi tid
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' },
+
# minmax float
{ amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4',
amprocrighttype => 'float4', amprocnum => '1',
@@ -1142,6 +1322,80 @@
amprocrighttype => 'float4', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi float
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float4' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+
# bloom float
{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
amprocrighttype => 'float4', amprocnum => '1',
@@ -1195,6 +1449,26 @@
amprocrighttype => 'macaddr', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi macaddr
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_macaddr' },
+
# bloom macaddr
{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
amprocrighttype => 'macaddr', amprocnum => '1',
@@ -1229,6 +1503,26 @@
amprocrighttype => 'macaddr8', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi macaddr8
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_macaddr8' },
+
# bloom macaddr8
{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
amprocrighttype => 'macaddr8', amprocnum => '1',
@@ -1262,6 +1556,26 @@
{ amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' },
+# minmax multi inet
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_inet' },
+
# bloom inet
{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '1',
@@ -1347,6 +1661,25 @@
{ amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time',
amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' },
+# minmax multi time without time zone
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_time' },
+
# bloom time without time zone
{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
amprocrighttype => 'time', amprocnum => '1',
@@ -1472,6 +1805,170 @@
amprocrighttype => 'timestamptz', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi datetime (date, timestamp, timestamptz)
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+
# bloom datetime (date, timestamp, timestamptz)
{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
amprocrighttype => 'timestamp', amprocnum => '1',
@@ -1542,6 +2039,26 @@
amprocrighttype => 'interval', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi interval
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_interval' },
+
# bloom interval
{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
amprocrighttype => 'interval', amprocnum => '1',
@@ -1576,6 +2093,26 @@
amprocrighttype => 'timetz', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi time with time zone
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_timetz' },
+
# bloom time with time zone
{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
amprocrighttype => 'timetz', amprocnum => '1',
@@ -1636,6 +2173,26 @@
amprocrighttype => 'numeric', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi numeric
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_numeric' },
+
# bloom numeric
{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
amprocrighttype => 'numeric', amprocnum => '1',
@@ -1667,7 +2224,28 @@
amprocrighttype => 'uuid', amprocnum => '3',
amproc => 'brin_minmax_consistent' },
{ amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid',
- amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' },
+ amprocrighttype => 'uuid', amprocnum => '4',
+ amproc => 'brin_minmax_union' },
+
+# minmax multi uuid
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_uuid' },
# bloom uuid
{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
@@ -1722,6 +2300,26 @@
amprocrighttype => 'pg_lsn', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi pg_lsn
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_pg_lsn' },
+
# bloom pg_lsn
{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
amprocrighttype => 'pg_lsn', amprocnum => '1',
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ca747a03b9..27ef11b81b 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -275,18 +275,27 @@
{ opcmethod => 'brin', opcname => 'int8_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8',
opckeytype => 'int8' },
+{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops',
+ opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8',
+ opckeytype => 'int8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int8_bloom_ops',
opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8',
opckeytype => 'int8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int2_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2',
opckeytype => 'int2' },
+{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops',
+ opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2',
+ opckeytype => 'int2', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int2_bloom_ops',
opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2',
opckeytype => 'int2', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int4_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4',
opckeytype => 'int4' },
+{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops',
+ opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4',
+ opckeytype => 'int4', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int4_bloom_ops',
opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4',
opckeytype => 'int4', opcdefault => 'f' },
@@ -298,6 +307,9 @@
opckeytype => 'text', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'oid_minmax_ops',
opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' },
+{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops',
+ opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid',
+ opckeytype => 'oid', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'oid_bloom_ops',
opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid',
opckeytype => 'oid', opcdefault => 'f' },
@@ -306,33 +318,51 @@
{ opcmethod => 'brin', opcname => 'tid_bloom_ops',
opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid',
opcdefault => 'f'},
+{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops',
+ opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid',
+ opckeytype => 'tid', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'float4_minmax_ops',
opcfamily => 'brin/float_minmax_ops', opcintype => 'float4',
opckeytype => 'float4' },
+{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops',
+ opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4',
+ opckeytype => 'float4', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'float4_bloom_ops',
opcfamily => 'brin/float_bloom_ops', opcintype => 'float4',
opckeytype => 'float4', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'float8_minmax_ops',
opcfamily => 'brin/float_minmax_ops', opcintype => 'float8',
opckeytype => 'float8' },
+{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops',
+ opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8',
+ opckeytype => 'float8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'float8_bloom_ops',
opcfamily => 'brin/float_bloom_ops', opcintype => 'float8',
opckeytype => 'float8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr_minmax_ops',
opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr',
opckeytype => 'macaddr' },
+{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops',
+ opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr',
+ opckeytype => 'macaddr', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops',
opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr',
opckeytype => 'macaddr', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr8_minmax_ops',
opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8',
opckeytype => 'macaddr8' },
+{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops',
+ opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8',
+ opckeytype => 'macaddr8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops',
opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8',
opckeytype => 'macaddr8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'inet_minmax_ops',
opcfamily => 'brin/network_minmax_ops', opcintype => 'inet',
opcdefault => 'f', opckeytype => 'inet' },
+{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops',
+ opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet',
+ opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'inet_bloom_ops',
opcfamily => 'brin/network_bloom_ops', opcintype => 'inet',
opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' },
@@ -348,36 +378,54 @@
{ opcmethod => 'brin', opcname => 'time_minmax_ops',
opcfamily => 'brin/time_minmax_ops', opcintype => 'time',
opckeytype => 'time' },
+{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops',
+ opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time',
+ opckeytype => 'time', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'time_bloom_ops',
opcfamily => 'brin/time_bloom_ops', opcintype => 'time',
opckeytype => 'time', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'date_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date',
opckeytype => 'date' },
+{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops',
+ opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date',
+ opckeytype => 'date', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'date_bloom_ops',
opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date',
opckeytype => 'date', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamp_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp',
opckeytype => 'timestamp' },
+{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops',
+ opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp',
+ opckeytype => 'timestamp', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops',
opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp',
opckeytype => 'timestamp', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamptz_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz',
opckeytype => 'timestamptz' },
+{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops',
+ opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz',
+ opckeytype => 'timestamptz', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops',
opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz',
opckeytype => 'timestamptz', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'interval_minmax_ops',
opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval',
opckeytype => 'interval' },
+{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops',
+ opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval',
+ opckeytype => 'interval', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'interval_bloom_ops',
opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval',
opckeytype => 'interval', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timetz_minmax_ops',
opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz',
opckeytype => 'timetz' },
+{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops',
+ opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz',
+ opckeytype => 'timetz', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timetz_bloom_ops',
opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz',
opckeytype => 'timetz', opcdefault => 'f' },
@@ -389,6 +437,9 @@
{ opcmethod => 'brin', opcname => 'numeric_minmax_ops',
opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric',
opckeytype => 'numeric' },
+{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops',
+ opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric',
+ opckeytype => 'numeric', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'numeric_bloom_ops',
opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric',
opckeytype => 'numeric', opcdefault => 'f' },
@@ -398,6 +449,9 @@
{ opcmethod => 'brin', opcname => 'uuid_minmax_ops',
opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid',
opckeytype => 'uuid' },
+{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops',
+ opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid',
+ opckeytype => 'uuid', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'uuid_bloom_ops',
opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid',
opckeytype => 'uuid', opcdefault => 'f' },
@@ -407,6 +461,9 @@
{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops',
opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn',
opckeytype => 'pg_lsn' },
+{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops',
+ opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn',
+ opckeytype => 'pg_lsn', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops',
opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn',
opckeytype => 'pg_lsn', opcdefault => 'f' },
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 8875079698..8e6d9eec16 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -180,10 +180,14 @@
opfmethod => 'gin', opfname => 'jsonb_path_ops' },
{ oid => '4054',
opfmethod => 'brin', opfname => 'integer_minmax_ops' },
+{ oid => '9015',
+ opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' },
{ oid => '8100',
opfmethod => 'brin', opfname => 'integer_bloom_ops' },
{ oid => '4055',
opfmethod => 'brin', opfname => 'numeric_minmax_ops' },
+{ oid => '9016',
+ opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' },
{ oid => '4056',
opfmethod => 'brin', opfname => 'text_minmax_ops' },
{ oid => '8101',
@@ -192,10 +196,14 @@
opfmethod => 'brin', opfname => 'numeric_bloom_ops' },
{ oid => '4058',
opfmethod => 'brin', opfname => 'timetz_minmax_ops' },
+{ oid => '9017',
+ opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' },
{ oid => '8103',
opfmethod => 'brin', opfname => 'timetz_bloom_ops' },
{ oid => '4059',
opfmethod => 'brin', opfname => 'datetime_minmax_ops' },
+{ oid => '9018',
+ opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' },
{ oid => '8104',
opfmethod => 'brin', opfname => 'datetime_bloom_ops' },
{ oid => '4062',
@@ -212,26 +220,38 @@
opfmethod => 'brin', opfname => 'name_bloom_ops' },
{ oid => '4068',
opfmethod => 'brin', opfname => 'oid_minmax_ops' },
+{ oid => '9019',
+ opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' },
{ oid => '8108',
opfmethod => 'brin', opfname => 'oid_bloom_ops' },
{ oid => '4069',
opfmethod => 'brin', opfname => 'tid_minmax_ops' },
{ oid => '8123',
opfmethod => 'brin', opfname => 'tid_bloom_ops' },
+{ oid => '9020',
+ opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' },
{ oid => '4070',
opfmethod => 'brin', opfname => 'float_minmax_ops' },
+{ oid => '9021',
+ opfmethod => 'brin', opfname => 'float_minmax_multi_ops' },
{ oid => '8109',
opfmethod => 'brin', opfname => 'float_bloom_ops' },
{ oid => '4074',
opfmethod => 'brin', opfname => 'macaddr_minmax_ops' },
+{ oid => '9022',
+ opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' },
{ oid => '8110',
opfmethod => 'brin', opfname => 'macaddr_bloom_ops' },
{ oid => '4109',
opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' },
+{ oid => '9023',
+ opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' },
{ oid => '8111',
opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' },
{ oid => '4075',
opfmethod => 'brin', opfname => 'network_minmax_ops' },
+{ oid => '9024',
+ opfmethod => 'brin', opfname => 'network_minmax_multi_ops' },
{ oid => '4102',
opfmethod => 'brin', opfname => 'network_inclusion_ops' },
{ oid => '8112',
@@ -242,10 +262,14 @@
opfmethod => 'brin', opfname => 'bpchar_bloom_ops' },
{ oid => '4077',
opfmethod => 'brin', opfname => 'time_minmax_ops' },
+{ oid => '9025',
+ opfmethod => 'brin', opfname => 'time_minmax_multi_ops' },
{ oid => '8114',
opfmethod => 'brin', opfname => 'time_bloom_ops' },
{ oid => '4078',
opfmethod => 'brin', opfname => 'interval_minmax_ops' },
+{ oid => '9026',
+ opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' },
{ oid => '8115',
opfmethod => 'brin', opfname => 'interval_bloom_ops' },
{ oid => '4079',
@@ -254,12 +278,16 @@
opfmethod => 'brin', opfname => 'varbit_minmax_ops' },
{ oid => '4081',
opfmethod => 'brin', opfname => 'uuid_minmax_ops' },
+{ oid => '9027',
+ opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' },
{ oid => '8116',
opfmethod => 'brin', opfname => 'uuid_bloom_ops' },
{ oid => '4103',
opfmethod => 'brin', opfname => 'range_inclusion_ops' },
{ oid => '4082',
opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' },
+{ oid => '9028',
+ opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' },
{ oid => '8117',
opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' },
{ oid => '4104',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index dc17d4ce38..77735c3abe 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8110,6 +8110,71 @@
proname => 'brin_minmax_union', prorettype => 'bool',
proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' },
+# BRIN minmax multi
+{ oid => '9029', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal',
+ proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' },
+{ oid => '9030', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_add_value', prorettype => 'bool',
+ proargtypes => 'internal internal internal internal',
+ prosrc => 'brin_minmax_multi_add_value' },
+{ oid => '9031', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_consistent', prorettype => 'bool',
+ proargtypes => 'internal internal internal int4',
+ prosrc => 'brin_minmax_multi_consistent' },
+{ oid => '9032', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_union', prorettype => 'bool',
+ proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' },
+{ oid => '9033', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f',
+ proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' },
+
+{ oid => '9000', descr => 'BRIN multi minmax int2 distance',
+ proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' },
+{ oid => '9001', descr => 'BRIN multi minmax int4 distance',
+ proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' },
+{ oid => '9002', descr => 'BRIN multi minmax int8 distance',
+ proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' },
+{ oid => '9003', descr => 'BRIN multi minmax float4 distance',
+ proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' },
+{ oid => '9004', descr => 'BRIN multi minmax float8 distance',
+ proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' },
+{ oid => '9005', descr => 'BRIN multi minmax numeric distance',
+ proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' },
+{ oid => '9006', descr => 'BRIN multi minmax tid distance',
+ proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' },
+{ oid => '9007', descr => 'BRIN multi minmax uuid distance',
+ proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' },
+{ oid => '9008', descr => 'BRIN multi minmax time distance',
+ proname => 'brin_minmax_multi_distance_time', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' },
+{ oid => '9009', descr => 'BRIN multi minmax interval distance',
+ proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' },
+{ oid => '9010', descr => 'BRIN multi minmax timetz distance',
+ proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' },
+{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance',
+ proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' },
+{ oid => '9012', descr => 'BRIN multi minmax macaddr distance',
+ proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' },
+{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance',
+ proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' },
+{ oid => '9014', descr => 'BRIN multi minmax inet distance',
+ proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' },
+
# BRIN inclusion
{ oid => '4105', descr => 'BRIN inclusion support',
proname => 'brin_inclusion_opcinfo', prorettype => 'internal',
@@ -11007,3 +11072,18 @@
{ oid => '9038', descr => 'I/O',
proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea',
proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' },
+
+
+{ oid => '9040', descr => 'I/O',
+ proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary',
+ proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' },
+{ oid => '9041', descr => 'I/O',
+ proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring',
+ proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' },
+{ oid => '9042', descr => 'I/O',
+ proname => 'brin_minmax_multi_summary_recv', provolatile => 's',
+ prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal',
+ prosrc => 'brin_minmax_multi_summary_recv' },
+{ oid => '9043', descr => 'I/O',
+ proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index a41c2e5418..c189b35a3d 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -638,3 +638,10 @@
typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out',
typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send',
typalign => 'i', typstorage => 'x', typcollation => 'default' },
+
+{ oid => '9039', oid_symbol => 'BRINMINMAXMULTISUMMARYOID',
+ descr => 'BRIN minmax-multi summary',
+ typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S',
+ typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out',
+ typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send',
+ typalign => 'i', typstorage => 'x', typcollation => 'default' },
diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out
new file mode 100644
index 0000000000..966dc4aadc
--- /dev/null
+++ b/src/test/regress/expected/brin_multi.out
@@ -0,0 +1,421 @@
+CREATE TABLE brintest_multi (
+ int8col bigint,
+ int2col smallint,
+ int4col integer,
+ oidcol oid,
+ tidcol tid,
+ float4col real,
+ float8col double precision,
+ macaddrcol macaddr,
+ inetcol inet,
+ cidrcol cidr,
+ datecol date,
+ timecol time without time zone,
+ timestampcol timestamp without time zone,
+ timestamptzcol timestamp with time zone,
+ intervalcol interval,
+ timetzcol time with time zone,
+ numericcol numeric,
+ uuidcol uuid,
+ lsncol pg_lsn
+) WITH (fillfactor=10);
+INSERT INTO brintest_multi SELECT
+ 142857 * tenthous,
+ thousand,
+ twothousand,
+ unique1::oid,
+ format('(%s,%s)', tenthous, twenty)::tid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4/24' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20+02' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 100;
+-- throw in some NULL's and different values
+INSERT INTO brintest_multi (inetcol, cidrcol) SELECT
+ inet 'fe80::6e40:8ff:fea9:8c46' + tenthous,
+ cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous
+FROM tenk1 ORDER BY thousand, tenthous LIMIT 25;
+-- test minmax-multi specific index options
+-- number of values must be >= 16
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops(values_per_range = 15)
+);
+ERROR: value 15 out of bounds for option "values_per_range"
+DETAIL: Valid values are between "16" and "256".
+-- number of values must be <= 256
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops(values_per_range = 257)
+);
+ERROR: value 257 out of bounds for option "values_per_range"
+DETAIL: Valid values are between "16" and "256".
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops,
+ int2col int2_minmax_multi_ops,
+ int4col int4_minmax_multi_ops,
+ oidcol oid_minmax_multi_ops,
+ tidcol tid_minmax_multi_ops,
+ float4col float4_minmax_multi_ops,
+ float8col float8_minmax_multi_ops,
+ macaddrcol macaddr_minmax_multi_ops,
+ inetcol inet_minmax_multi_ops,
+ cidrcol inet_minmax_multi_ops,
+ datecol date_minmax_multi_ops,
+ timecol time_minmax_multi_ops,
+ timestampcol timestamp_minmax_multi_ops,
+ timestamptzcol timestamptz_minmax_multi_ops,
+ intervalcol interval_minmax_multi_ops,
+ timetzcol timetz_minmax_multi_ops,
+ numericcol numeric_minmax_multi_ops,
+ uuidcol uuid_minmax_multi_ops,
+ lsncol pg_lsn_minmax_multi_ops
+) with (pages_per_range = 1);
+CREATE TABLE brinopers_multi (colname name, typ text,
+ op text[], value text[], matches int[],
+ check (cardinality(op) = cardinality(value)),
+ check (cardinality(op) = cardinality(matches)));
+INSERT INTO brinopers_multi VALUES
+ ('int2col', 'int2',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int2col', 'int4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int2col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int2',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('int8col', 'int2',
+ '{>, >=}',
+ '{0, 0}',
+ '{100, 100}'),
+ ('int8col', 'int4',
+ '{>, >=}',
+ '{0, 0}',
+ '{100, 100}'),
+ ('int8col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 1257141600, 1428427143, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('oidcol', 'oid',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 8800, 9999, 9999}',
+ '{100, 100, 1, 100, 100}'),
+ ('tidcol', 'tid',
+ '{>, >=, =, <=, <}',
+ '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}',
+ '{100, 100, 1, 100, 100}'),
+ ('float4col', 'float4',
+ '{>, >=, =, <=, <}',
+ '{0.0103093, 0.0103093, 1, 1, 1}',
+ '{100, 100, 4, 100, 96}'),
+ ('float4col', 'float8',
+ '{>, >=, =, <=, <}',
+ '{0.0103093, 0.0103093, 1, 1, 1}',
+ '{100, 100, 4, 100, 96}'),
+ ('float8col', 'float4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 0, 1.98, 1.98}',
+ '{99, 100, 1, 100, 100}'),
+ ('float8col', 'float8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 0, 1.98, 1.98}',
+ '{99, 100, 1, 100, 100}'),
+ ('macaddrcol', 'macaddr',
+ '{>, >=, =, <=, <}',
+ '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}',
+ '{99, 100, 2, 100, 100}'),
+ ('inetcol', 'inet',
+ '{=, <, <=, >, >=}',
+ '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{1, 100, 100, 125, 125}'),
+ ('inetcol', 'cidr',
+ '{<, <=, >, >=}',
+ '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{100, 100, 125, 125}'),
+ ('cidrcol', 'inet',
+ '{=, <, <=, >, >=}',
+ '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{2, 100, 100, 125, 125}'),
+ ('cidrcol', 'cidr',
+ '{=, <, <=, >, >=}',
+ '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{2, 100, 100, 125, 125}'),
+ ('datecol', 'date',
+ '{>, >=, =, <=, <}',
+ '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}',
+ '{100, 100, 1, 100, 100}'),
+ ('timecol', 'time',
+ '{>, >=, =, <=, <}',
+ '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestampcol', 'timestamp',
+ '{>, >=, =, <=, <}',
+ '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestampcol', 'timestamptz',
+ '{>, >=, =, <=, <}',
+ '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestamptzcol', 'timestamptz',
+ '{>, >=, =, <=, <}',
+ '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}',
+ '{100, 100, 1, 100, 100}'),
+ ('intervalcol', 'interval',
+ '{>, >=, =, <=, <}',
+ '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}',
+ '{100, 100, 1, 100, 100}'),
+ ('timetzcol', 'timetz',
+ '{>, >=, =, <=, <}',
+ '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}',
+ '{99, 100, 2, 100, 100}'),
+ ('numericcol', 'numeric',
+ '{>, >=, =, <=, <}',
+ '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}',
+ '{100, 100, 1, 100, 100}'),
+ ('uuidcol', 'uuid',
+ '{>, >=, =, <=, <}',
+ '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}',
+ '{100, 100, 1, 100, 100}'),
+ ('lsncol', 'pg_lsn',
+ '{>, >=, =, <=, <, IS, IS NOT}',
+ '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}',
+ '{100, 100, 1, 100, 100, 25, 100}');
+DO $x$
+DECLARE
+ r record;
+ r2 record;
+ cond text;
+ idx_ctids tid[];
+ ss_ctids tid[];
+ count int;
+ plan_ok bool;
+ plan_line text;
+BEGIN
+ FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP
+
+ -- prepare the condition
+ IF r.value IS NULL THEN
+ cond := format('%I %s %L', r.colname, r.oper, r.value);
+ ELSE
+ cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ);
+ END IF;
+
+ -- run the query using the brin index
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get bitmap indexscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond)
+ INTO idx_ctids;
+
+ -- run the query using a seqscan
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get seqscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond)
+ INTO ss_ctids;
+
+ -- make sure both return the same results
+ count := array_length(idx_ctids, 1);
+
+ IF NOT (count = array_length(ss_ctids, 1) AND
+ idx_ctids @> ss_ctids AND
+ idx_ctids <@ ss_ctids) THEN
+ -- report the results of each scan to make the differences obvious
+ RAISE WARNING 'something not right in %: count %', r, count;
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP
+ RAISE NOTICE 'seqscan: %', r2;
+ END LOOP;
+
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP
+ RAISE NOTICE 'bitmapscan: %', r2;
+ END LOOP;
+ END IF;
+
+ -- make sure we found expected number of matches
+ IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF;
+ END LOOP;
+END;
+$x$;
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+INSERT INTO brintest_multi SELECT
+ 142857 * tenthous,
+ thousand,
+ twothousand,
+ unique1::oid,
+ format('(%s,%s)', tenthous, twenty)::tid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5;
+SELECT brin_desummarize_range('brinidx_multi', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+VACUUM brintest_multi; -- force a summarization cycle in brinidx
+UPDATE brintest_multi SET int8col = int8col * int4col;
+-- Tests for brin_summarize_new_values
+SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index
+ERROR: "brintest_multi" is not an index
+SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
+ERROR: "tenk1_unique1" is not a BRIN index
+SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected
+ brin_summarize_new_values
+---------------------------
+ 0
+(1 row)
+
+-- Tests for brin_desummarize_range
+SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range
+ERROR: block number out of range: -1
+SELECT brin_desummarize_range('brinidx_multi', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+SELECT brin_desummarize_range('brinidx_multi', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+SELECT brin_desummarize_range('brinidx_multi', 100000000);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+-- Test brin_summarize_range
+CREATE TABLE brin_summarize_multi (
+ value int
+) WITH (fillfactor=10, autovacuum_enabled=false);
+CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2);
+-- Fill a few pages
+DO $$
+DECLARE curtid tid;
+BEGIN
+ LOOP
+ INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid;
+ EXIT WHEN curtid > tid '(2, 0)';
+ END LOOP;
+END;
+$$;
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_multi_idx', 0);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- nothing: already summarized
+SELECT brin_summarize_range('brin_summarize_multi_idx', 1);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_multi_idx', 2);
+ brin_summarize_range
+----------------------
+ 1
+(1 row)
+
+-- nothing: page doesn't exist in table
+SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- invalid block number values
+SELECT brin_summarize_range('brin_summarize_multi_idx', -1);
+ERROR: block number out of range: -1
+SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296);
+ERROR: block number out of range: 4294967296
+-- test brin cost estimates behave sanely based on correlation of values
+CREATE TABLE brin_test_multi (a INT, b INT);
+INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x);
+CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2);
+CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2);
+VACUUM ANALYZE brin_test_multi;
+-- Ensure brin index is used when columns are perfectly correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1;
+ QUERY PLAN
+--------------------------------------------------
+ Bitmap Heap Scan on brin_test_multi
+ Recheck Cond: (a = 1)
+ -> Bitmap Index Scan on brin_test_multi_a_idx
+ Index Cond: (a = 1)
+(4 rows)
+
+-- Ensure brin index is not used when values are not correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1;
+ QUERY PLAN
+-----------------------------
+ Seq Scan on brin_test_multi
+ Filter: (b = 1)
+(2 rows)
+
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 72c7598c89..f855633634 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4986,12 +4986,13 @@ List of access methods
(0 rows)
\dAc brin pg*.oid*
- List of operator classes
- AM | Input type | Storage type | Operator class | Default?
-------+------------+--------------+----------------+----------
- brin | oid | | oid_bloom_ops | no
- brin | oid | | oid_minmax_ops | yes
-(2 rows)
+ List of operator classes
+ AM | Input type | Storage type | Operator class | Default?
+------+------------+--------------+----------------------+----------
+ brin | oid | | oid_bloom_ops | no
+ brin | oid | | oid_minmax_multi_ops | no
+ brin | oid | | oid_minmax_ops | yes
+(3 rows)
\dAf spgist
List of operator families
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 97bf9797de..55a30a6b47 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%'
(SELECT 1 FROM pg_type as p2
WHERE p2.typname = ('_' || p1.typname)::name AND
p2.typelem = p1.oid and p1.typarray = p2.oid);
- oid | typname
-------+-----------------------
+ oid | typname
+------+------------------------------
194 | pg_node_tree
3361 | pg_ndistinct
3402 | pg_dependencies
5017 | pg_mcv_list
9034 | pg_brin_bloom_summary
-(5 rows)
+ 9039 | pg_brin_minmax_multi_summary
+(6 rows)
-- Make sure typarray points to a varlena array type of our own base
SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index b49239b1d0..a933db5456 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview
# ----------
# Additional BRIN tests
# ----------
-test: brin_bloom
+test: brin_bloom brin_multi
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index abce8e180a..3f05a7061f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -108,6 +108,7 @@ test: namespace
test: prepared_xacts
test: brin
test: brin_bloom
+test: brin_multi
test: gin
test: gist
test: spgist
diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql
new file mode 100644
index 0000000000..9de6ddc6d7
--- /dev/null
+++ b/src/test/regress/sql/brin_multi.sql
@@ -0,0 +1,371 @@
+CREATE TABLE brintest_multi (
+ int8col bigint,
+ int2col smallint,
+ int4col integer,
+ oidcol oid,
+ tidcol tid,
+ float4col real,
+ float8col double precision,
+ macaddrcol macaddr,
+ inetcol inet,
+ cidrcol cidr,
+ datecol date,
+ timecol time without time zone,
+ timestampcol timestamp without time zone,
+ timestamptzcol timestamp with time zone,
+ intervalcol interval,
+ timetzcol time with time zone,
+ numericcol numeric,
+ uuidcol uuid,
+ lsncol pg_lsn
+) WITH (fillfactor=10);
+
+INSERT INTO brintest_multi SELECT
+ 142857 * tenthous,
+ thousand,
+ twothousand,
+ unique1::oid,
+ format('(%s,%s)', tenthous, twenty)::tid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4/24' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20+02' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 100;
+
+-- throw in some NULL's and different values
+INSERT INTO brintest_multi (inetcol, cidrcol) SELECT
+ inet 'fe80::6e40:8ff:fea9:8c46' + tenthous,
+ cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous
+FROM tenk1 ORDER BY thousand, tenthous LIMIT 25;
+
+-- test minmax-multi specific index options
+-- number of values must be >= 16
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops(values_per_range = 15)
+);
+-- number of values must be <= 256
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops(values_per_range = 257)
+);
+
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops,
+ int2col int2_minmax_multi_ops,
+ int4col int4_minmax_multi_ops,
+ oidcol oid_minmax_multi_ops,
+ tidcol tid_minmax_multi_ops,
+ float4col float4_minmax_multi_ops,
+ float8col float8_minmax_multi_ops,
+ macaddrcol macaddr_minmax_multi_ops,
+ inetcol inet_minmax_multi_ops,
+ cidrcol inet_minmax_multi_ops,
+ datecol date_minmax_multi_ops,
+ timecol time_minmax_multi_ops,
+ timestampcol timestamp_minmax_multi_ops,
+ timestamptzcol timestamptz_minmax_multi_ops,
+ intervalcol interval_minmax_multi_ops,
+ timetzcol timetz_minmax_multi_ops,
+ numericcol numeric_minmax_multi_ops,
+ uuidcol uuid_minmax_multi_ops,
+ lsncol pg_lsn_minmax_multi_ops
+) with (pages_per_range = 1);
+
+CREATE TABLE brinopers_multi (colname name, typ text,
+ op text[], value text[], matches int[],
+ check (cardinality(op) = cardinality(value)),
+ check (cardinality(op) = cardinality(matches)));
+
+INSERT INTO brinopers_multi VALUES
+ ('int2col', 'int2',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int2col', 'int4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int2col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int2',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('int8col', 'int2',
+ '{>, >=}',
+ '{0, 0}',
+ '{100, 100}'),
+ ('int8col', 'int4',
+ '{>, >=}',
+ '{0, 0}',
+ '{100, 100}'),
+ ('int8col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 1257141600, 1428427143, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('oidcol', 'oid',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 8800, 9999, 9999}',
+ '{100, 100, 1, 100, 100}'),
+ ('tidcol', 'tid',
+ '{>, >=, =, <=, <}',
+ '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}',
+ '{100, 100, 1, 100, 100}'),
+ ('float4col', 'float4',
+ '{>, >=, =, <=, <}',
+ '{0.0103093, 0.0103093, 1, 1, 1}',
+ '{100, 100, 4, 100, 96}'),
+ ('float4col', 'float8',
+ '{>, >=, =, <=, <}',
+ '{0.0103093, 0.0103093, 1, 1, 1}',
+ '{100, 100, 4, 100, 96}'),
+ ('float8col', 'float4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 0, 1.98, 1.98}',
+ '{99, 100, 1, 100, 100}'),
+ ('float8col', 'float8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 0, 1.98, 1.98}',
+ '{99, 100, 1, 100, 100}'),
+ ('macaddrcol', 'macaddr',
+ '{>, >=, =, <=, <}',
+ '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}',
+ '{99, 100, 2, 100, 100}'),
+ ('inetcol', 'inet',
+ '{=, <, <=, >, >=}',
+ '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{1, 100, 100, 125, 125}'),
+ ('inetcol', 'cidr',
+ '{<, <=, >, >=}',
+ '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{100, 100, 125, 125}'),
+ ('cidrcol', 'inet',
+ '{=, <, <=, >, >=}',
+ '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{2, 100, 100, 125, 125}'),
+ ('cidrcol', 'cidr',
+ '{=, <, <=, >, >=}',
+ '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{2, 100, 100, 125, 125}'),
+ ('datecol', 'date',
+ '{>, >=, =, <=, <}',
+ '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}',
+ '{100, 100, 1, 100, 100}'),
+ ('timecol', 'time',
+ '{>, >=, =, <=, <}',
+ '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestampcol', 'timestamp',
+ '{>, >=, =, <=, <}',
+ '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestampcol', 'timestamptz',
+ '{>, >=, =, <=, <}',
+ '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestamptzcol', 'timestamptz',
+ '{>, >=, =, <=, <}',
+ '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}',
+ '{100, 100, 1, 100, 100}'),
+ ('intervalcol', 'interval',
+ '{>, >=, =, <=, <}',
+ '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}',
+ '{100, 100, 1, 100, 100}'),
+ ('timetzcol', 'timetz',
+ '{>, >=, =, <=, <}',
+ '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}',
+ '{99, 100, 2, 100, 100}'),
+ ('numericcol', 'numeric',
+ '{>, >=, =, <=, <}',
+ '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}',
+ '{100, 100, 1, 100, 100}'),
+ ('uuidcol', 'uuid',
+ '{>, >=, =, <=, <}',
+ '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}',
+ '{100, 100, 1, 100, 100}'),
+ ('lsncol', 'pg_lsn',
+ '{>, >=, =, <=, <, IS, IS NOT}',
+ '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}',
+ '{100, 100, 1, 100, 100, 25, 100}');
+
+DO $x$
+DECLARE
+ r record;
+ r2 record;
+ cond text;
+ idx_ctids tid[];
+ ss_ctids tid[];
+ count int;
+ plan_ok bool;
+ plan_line text;
+BEGIN
+ FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP
+
+ -- prepare the condition
+ IF r.value IS NULL THEN
+ cond := format('%I %s %L', r.colname, r.oper, r.value);
+ ELSE
+ cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ);
+ END IF;
+
+ -- run the query using the brin index
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get bitmap indexscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond)
+ INTO idx_ctids;
+
+ -- run the query using a seqscan
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get seqscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond)
+ INTO ss_ctids;
+
+ -- make sure both return the same results
+ count := array_length(idx_ctids, 1);
+
+ IF NOT (count = array_length(ss_ctids, 1) AND
+ idx_ctids @> ss_ctids AND
+ idx_ctids <@ ss_ctids) THEN
+ -- report the results of each scan to make the differences obvious
+ RAISE WARNING 'something not right in %: count %', r, count;
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP
+ RAISE NOTICE 'seqscan: %', r2;
+ END LOOP;
+
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP
+ RAISE NOTICE 'bitmapscan: %', r2;
+ END LOOP;
+ END IF;
+
+ -- make sure we found expected number of matches
+ IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF;
+ END LOOP;
+END;
+$x$;
+
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+
+INSERT INTO brintest_multi SELECT
+ 142857 * tenthous,
+ thousand,
+ twothousand,
+ unique1::oid,
+ format('(%s,%s)', tenthous, twenty)::tid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5;
+
+SELECT brin_desummarize_range('brinidx_multi', 0);
+VACUUM brintest_multi; -- force a summarization cycle in brinidx
+
+UPDATE brintest_multi SET int8col = int8col * int4col;
+
+-- Tests for brin_summarize_new_values
+SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index
+SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
+SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected
+
+-- Tests for brin_desummarize_range
+SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range
+SELECT brin_desummarize_range('brinidx_multi', 0);
+SELECT brin_desummarize_range('brinidx_multi', 0);
+SELECT brin_desummarize_range('brinidx_multi', 100000000);
+
+-- Test brin_summarize_range
+CREATE TABLE brin_summarize_multi (
+ value int
+) WITH (fillfactor=10, autovacuum_enabled=false);
+CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2);
+-- Fill a few pages
+DO $$
+DECLARE curtid tid;
+BEGIN
+ LOOP
+ INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid;
+ EXIT WHEN curtid > tid '(2, 0)';
+ END LOOP;
+END;
+$$;
+
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_multi_idx', 0);
+-- nothing: already summarized
+SELECT brin_summarize_range('brin_summarize_multi_idx', 1);
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_multi_idx', 2);
+-- nothing: page doesn't exist in table
+SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295);
+-- invalid block number values
+SELECT brin_summarize_range('brin_summarize_multi_idx', -1);
+SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296);
+
+
+-- test brin cost estimates behave sanely based on correlation of values
+CREATE TABLE brin_test_multi (a INT, b INT);
+INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x);
+CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2);
+CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2);
+VACUUM ANALYZE brin_test_multi;
+
+-- Ensure brin index is used when columns are perfectly correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1;
+-- Ensure brin index is not used when values are not correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1;
--
2.25.4
From 38f2d03d252595fe007b65fc097cc688b3909931 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 12 Sep 2020 15:07:59 +0200
Subject: [PATCH 5/5] Ignore correlation for new BRIN opclasses
The new BRIN opclasses (bloom and minmax-multi) are less sensitive to
poorly correlated data, so just assume the data is perfectly correlated
during costing.
---
src/backend/access/brin/brin_bloom.c | 1 +
src/backend/access/brin/brin_minmax_multi.c | 1 +
src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++-
src/include/access/brin_internal.h | 3 +++
4 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
index c2cbbd9400..03e9d4b713 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/src/backend/access/brin/brin_bloom.c
@@ -681,6 +681,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS)
result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) +
sizeof(BloomOpaque));
+ result->oi_ignore_correlation = true;
result->oi_nstored = 1;
result->oi_regular_nulls = true;
result->oi_opaque = (BloomOpaque *)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 227927fcf5..c8f36b0c8a 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -1306,6 +1306,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) +
sizeof(MinmaxMultiOpaque));
+ result->oi_ignore_correlation = true;
result->oi_nstored = 1;
result->oi_regular_nulls = true;
result->oi_opaque = (MinmaxMultiOpaque *)
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 00c7afc66f..c00265a66d 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -98,6 +98,7 @@
#include <math.h>
#include "access/brin.h"
+#include "access/brin_internal.h"
#include "access/brin_page.h"
#include "access/gin.h"
#include "access/table.h"
@@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double minimalRanges;
double estimatedRanges;
double selec;
- Relation indexRel;
+ Relation indexRel = NULL;
+ TupleDesc tupdesc = NULL;
ListCell *l;
VariableStatData vardata;
@@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
*/
indexRel = index_open(index->indexoid, NoLock);
brinGetStats(indexRel, &statsData);
+ tupdesc = RelationGetDescr(indexRel);
index_close(indexRel, NoLock);
/* work out the actual number of ranges in the index */
@@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
{
IndexClause *iclause = lfirst_node(IndexClause, l);
AttrNumber attnum = index->indexkeys[iclause->indexcol];
+ FmgrInfo *opcInfoFn;
+ BrinOpcInfo *opcInfo;
+ Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol);
+ bool ignore_correlation;
+
+ opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO);
+
+ opcInfo = (BrinOpcInfo *)
+ DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid));
+
+ ignore_correlation = opcInfo->oi_ignore_correlation;
/* attempt to lookup stats in relation for this index column */
if (attnum != 0)
@@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
if (sslot.nnumbers > 0)
varCorrelation = Abs(sslot.numbers[0]);
+ if (ignore_correlation)
+ varCorrelation = 1.0;
+
if (varCorrelation > *indexCorrelation)
*indexCorrelation = varCorrelation;
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index ee4d0706df..67aea62a02 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -30,6 +30,9 @@ typedef struct BrinOpcInfo
/* Regular processing of NULLs in BrinValues? */
bool oi_regular_nulls;
+ /* Ignore correlation during cost estimation */
+ bool oi_ignore_correlation;
+
/* Opaque pointer for the opclass' private use */
void *oi_opaque;
--
2.25.4
Attachments:
[text/plain] 0001-Pass-all-scan-keys-to-BRIN-consistent-funct-20200912.patch (23.5K, ../20200912161303.z54jctx5dw4srrh3@development/2-0001-Pass-all-scan-keys-to-BRIN-consistent-funct-20200912.patch)
download | inline diff:
From 2085561ad36af6994a81c1c6001fbd636077ed38 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 12 Sep 2020 15:07:04 +0200
Subject: [PATCH 1/5] Pass all scan keys to BRIN consistent function at once
Passing all scan keys to the BRIN consistent function at once may allow
elimination of additional ranges, which would be impossible when only
passing individual scan keys.
The code continues to support both the original (one scan key at a time)
and new (all scan keys at once) approaches, depending on whether the
consistent function accepts three or four arguments.
This modifies the existing BRIN opclasses (minmax, inclusion) although
those don't really benefit from this change. The primary purpose of this
is to allow more advanced opclases in the future.
Author: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com>
Reviewed-by: Mark Dilger <hornschnorter@gmail.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
---
src/backend/access/brin/brin.c | 150 +++++++++++++++-----
src/backend/access/brin/brin_inclusion.c | 170 +++++++++++++++--------
src/backend/access/brin/brin_minmax.c | 121 +++++++++++-----
src/backend/access/brin/brin_validate.c | 4 +-
src/include/catalog/pg_proc.dat | 4 +-
5 files changed, 324 insertions(+), 125 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 1f72562c60..ccf609e799 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -389,6 +389,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;
@@ -410,6 +413,61 @@ 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.
+ */
+ 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 index attribute, so init as needed. */
+ 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. This may waste
+ * a bit of memory, but we only expect small number of
+ * scan keys in general, so this should be negligible,
+ * and it's cheaper than having to repalloc repeatedly.
+ */
+ 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);
@@ -470,7 +528,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
else
{
- int keyno;
+ int attno;
/*
* Compare scan keys with summary values stored for the range.
@@ -480,51 +538,75 @@ 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;
- /*
- * 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));
+ /* skip attributes without any san keys */
+ if (nkeys[attno - 1] == 0)
+ continue;
- /* First time this column? look up consistent function */
- if (consistentFn[keyattno - 1].fn_oid == InvalidOid)
- {
- FmgrInfo *tmp;
+ bval = &dtup->bt_columns[attno - 1];
- tmp = index_getprocinfo(idxRel, keyattno,
- BRIN_PROCNUM_CONSISTENT);
- fmgr_info_copy(&consistentFn[keyattno - 1], tmp,
- CurrentMemoryContext);
- }
+ 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.
+ * 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,
*/
- add = FunctionCall3Coll(&consistentFn[keyattno - 1],
- key->sk_collation,
- PointerGetDatum(bdesc),
- PointerGetDatum(bval),
- PointerGetDatum(key));
- addrange = DatumGetBool(add);
+ if (consistentFn[attno - 1].fn_nargs >= 4)
+ {
+ Oid collation;
+
+ /*
+ * 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 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;
+ }
+ }
+
if (!addrange)
break;
}
diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c
index 7e380d66ed..853727190c 100644
--- a/src/backend/access/brin/brin_inclusion.c
+++ b/src/backend/access/brin/brin_inclusion.c
@@ -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);
/*
@@ -258,53 +260,109 @@ 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 regular_keys = false;
- /* Handle IS NULL/IS NOT NULL tests. */
- if (key->sk_flags & SK_ISNULL)
+ /*
+ * First check if there are any IS NULL scan keys, and if we're
+ * violating them. In that case we can terminate early, without
+ * inspecting the ranges.
+ */
+ for (keyno = 0; keyno < nkeys; keyno++)
{
- if (key->sk_flags & SK_SEARCHNULL)
+ ScanKey key = keys[keyno];
+
+ Assert(key->sk_attno == column->bv_attno);
+
+ /* handle IS NULL/IS NOT NULL tests */
+ if (key->sk_flags & SK_ISNULL)
{
- if (column->bv_allnulls || column->bv_hasnulls)
- PG_RETURN_BOOL(true);
- PG_RETURN_BOOL(false);
- }
+ if (key->sk_flags & SK_SEARCHNULL)
+ {
+ if (column->bv_allnulls || column->bv_hasnulls)
+ continue; /* this key is fine, continue */
- /*
- * For IS NOT NULL, we can only skip ranges that are known to have
- * only nulls.
- */
- if (key->sk_flags & SK_SEARCHNOTNULL)
- PG_RETURN_BOOL(!column->bv_allnulls);
+ PG_RETURN_BOOL(false);
+ }
- /*
- * Neither IS NULL nor IS NOT NULL was used; assume all indexable
- * operators are strict and return false.
- */
- PG_RETURN_BOOL(false);
+ /*
+ * For IS NOT NULL, we can only skip ranges that are known to have
+ * only nulls.
+ */
+ if (key->sk_flags & SK_SEARCHNOTNULL)
+ {
+ if (column->bv_allnulls)
+ PG_RETURN_BOOL(false);
+
+ continue;
+ }
+
+ /*
+ * Neither IS NULL nor IS NOT NULL was used; assume all indexable
+ * operators are strict and return false.
+ */
+ PG_RETURN_BOOL(false);
+ }
+ else
+ /* note we have regular (non-NULL) scan keys */
+ regular_keys = true;
}
- /* If it is all nulls, it cannot possibly be consistent. */
- if (column->bv_allnulls)
+ /*
+ * If the page range is all nulls, it cannot possibly be consistent if
+ * there are some regular scan keys.
+ */
+ if (column->bv_allnulls && regular_keys)
PG_RETURN_BOOL(false);
+ /* If there are no regular keys, the page range is considered consistent. */
+ if (!regular_keys)
+ PG_RETURN_BOOL(true);
+
/* It has to be checked, if it contains elements that are not mergeable. */
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 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 (!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;
+
switch (key->sk_strategy)
{
/*
@@ -324,49 +382,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
@@ -385,7 +443,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
@@ -406,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
@@ -425,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);
+ RTAdjacentStrategyNumber);
result = FunctionCall2Coll(finfo, colloid, unionval, query);
- PG_RETURN_DATUM(result);
+ return DatumGetBool(result);
/*
* Basic comparison strategies
@@ -460,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:
@@ -470,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;
}
}
diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c
index 4b5d6a7213..a3bb3558ec 100644
--- a/src/backend/access/brin/brin_minmax.c
+++ b/src/backend/access/brin/brin_minmax.c
@@ -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
@@ -146,47 +148,104 @@ 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 regular_keys = false;
- /* handle IS NULL/IS NOT NULL tests */
- if (key->sk_flags & SK_ISNULL)
+ /*
+ * First check if there are any IS NULL scan keys, and if we're
+ * violating them. In that case we can terminate early, without
+ * inspecting the ranges.
+ */
+ for (keyno = 0; keyno < nkeys; keyno++)
{
- if (key->sk_flags & SK_SEARCHNULL)
+ ScanKey key = keys[keyno];
+
+ Assert(key->sk_attno == column->bv_attno);
+
+ /* handle IS NULL/IS NOT NULL tests */
+ if (key->sk_flags & SK_ISNULL)
{
- if (column->bv_allnulls || column->bv_hasnulls)
- PG_RETURN_BOOL(true);
+ if (key->sk_flags & SK_SEARCHNULL)
+ {
+ if (column->bv_allnulls || column->bv_hasnulls)
+ continue; /* this key is fine, continue */
+
+ PG_RETURN_BOOL(false);
+ }
+
+ /*
+ * For IS NOT NULL, we can only skip ranges that are known to have
+ * only nulls.
+ */
+ if (key->sk_flags & SK_SEARCHNOTNULL)
+ {
+ if (column->bv_allnulls)
+ PG_RETURN_BOOL(false);
+
+ continue;
+ }
+
+ /*
+ * Neither IS NULL nor IS NOT NULL was used; assume all indexable
+ * operators are strict and return false.
+ */
PG_RETURN_BOOL(false);
}
+ else
+ /* note we have regular (non-NULL) scan keys */
+ regular_keys = true;
+ }
- /*
- * For IS NOT NULL, we can only skip ranges that are known to have
- * only nulls.
- */
- if (key->sk_flags & SK_SEARCHNOTNULL)
- PG_RETURN_BOOL(!column->bv_allnulls);
+ /*
+ * If the page range is all nulls, it cannot possibly be consistent if
+ * there are some regular scan keys.
+ */
+ if (column->bv_allnulls && regular_keys)
+ PG_RETURN_BOOL(false);
+
+ /* If there are no regular keys, the page range is considered consistent. */
+ if (!regular_keys)
+ PG_RETURN_BOOL(true);
- /*
- * Neither IS NULL nor IS NOT NULL was used; assume all indexable
- * operators are strict and return false.
+ /* 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.
*/
- PG_RETURN_BOOL(false);
+ if (!minmax_consistent_key(bdesc, column, key, colloid))
+ PG_RETURN_DATUM(false);;
}
- /* if the range is all empty, it cannot possibly be consistent */
- if (column->bv_allnulls)
- PG_RETURN_BOOL(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;
- attno = key->sk_attno;
- subtype = key->sk_subtype;
- value = key->sk_argument;
switch (key->sk_strategy)
{
case BTLessStrategyNumber:
@@ -229,7 +288,7 @@ brin_minmax_consistent(PG_FUNCTION_ARGS)
break;
}
- PG_RETURN_DATUM(matches);
+ return DatumGetBool(matches);
}
/*
diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c
index fb0615463e..0c28137c8f 100644
--- a/src/backend/access/brin/brin_validate.c
+++ b/src/backend/access/brin/brin_validate.c
@@ -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);
+ 3, 4, INTERNALOID, INTERNALOID,
+ INTERNALOID, INT4OID);
break;
case BRIN_PROCNUM_UNION:
ok = check_amproc_signature(procform->amproc, BOOLOID, true,
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 687509ba92..925b262b60 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8104,7 +8104,7 @@
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',
@@ -8120,7 +8120,7 @@
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',
--
2.25.4
[text/plain] 0002-Move-IS-NOT-NULL-handling-from-BRIN-support-20200912.patch (23.0K, ../20200912161303.z54jctx5dw4srrh3@development/3-0002-Move-IS-NOT-NULL-handling-from-BRIN-support-20200912.patch)
download | inline diff:
From d7812522e57ebb89aed73af5f5d736e380a8ac1f Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 12 Sep 2020 15:07:28 +0200
Subject: [PATCH 2/5] Move IS [NOT] NULL handling from BRIN support functions
The handling of IS [NOT] NULL clauses is independent of an opclass, and
most of the code was exactly the same in both minmax and inclusion. So
instead move the code from support procedures to the AM methods etc.
This simplifies the code quite a bit, and it simplifies the support
procedures quite a bit, as they don't need to care about NULL values and
flags at all.
Author: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Author: Nikita Glukhov <n.gluhov@postgrespro.ru>
Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com>
Reviewed-by: Mark Dilger <hornschnorter@gmail.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Reviewed-by: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
Reviewed-by: John Naylor <john.naylor@2ndquadrant.com>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
---
src/backend/access/brin/brin.c | 287 +++++++++++++++++------
src/backend/access/brin/brin_inclusion.c | 106 +--------
src/backend/access/brin/brin_minmax.c | 103 +-------
src/include/access/brin_internal.h | 3 +
4 files changed, 235 insertions(+), 264 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index ccf609e799..83c71d0f8e 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -35,6 +35,7 @@
#include "storage/freespace.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
#include "utils/rel.h"
@@ -77,7 +78,9 @@ static void form_and_insert_tuple(BrinBuildState *state);
static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
BrinTuple *b);
static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
-
+static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc,
+ BrinMemTuple *dtup, Datum *values, bool *nulls);
+static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys);
/*
* BRIN handler function: return IndexAmRoutine with access method parameters
@@ -178,7 +181,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
OffsetNumber off;
BrinTuple *brtup;
BrinMemTuple *dtup;
- int keyno;
CHECK_FOR_INTERRUPTS();
@@ -242,31 +244,7 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
dtup = brin_deform_tuple(bdesc, brtup, NULL);
- /*
- * Compare the key values of the new tuple to the stored index values;
- * our deformed tuple will get updated if the new tuple doesn't fit
- * the original range (note this means we can't break out of the loop
- * early). Make a note of whether this happens, so that we know to
- * insert the modified tuple later.
- */
- for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++)
- {
- Datum result;
- BrinValues *bval;
- FmgrInfo *addValue;
-
- bval = &dtup->bt_columns[keyno];
- addValue = index_getprocinfo(idxRel, keyno + 1,
- BRIN_PROCNUM_ADDVALUE);
- result = FunctionCall4Coll(addValue,
- idxRel->rd_indcollation[keyno],
- PointerGetDatum(bdesc),
- PointerGetDatum(bval),
- values[keyno],
- nulls[keyno]);
- /* if that returned true, we need to insert the updated tuple */
- need_insert |= DatumGetBool(result);
- }
+ need_insert = add_values_to_range(idxRel, bdesc, dtup, values, nulls);
if (!need_insert)
{
@@ -359,6 +337,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys)
return scan;
}
+
/*
* Execute the index scan.
*
@@ -389,8 +368,10 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
BrinMemTuple *dtup;
BrinTuple *btup = NULL;
Size btupsz = 0;
- ScanKey **keys;
- int *nkeys;
+ ScanKey **keys,
+ **nullkeys;
+ int *nkeys,
+ *nnullkeys;
int keyno;
opaque = (BrinOpaque *) scan->opaque;
@@ -415,10 +396,13 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
/*
* Make room for per-attribute lists of scan keys that we'll pass to the
- * consistent support procedure.
+ * consistent support procedure. We keep null and regular keys separate,
+ * so that we can easily pass regular keys to the consistent function.
*/
keys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts);
+ nullkeys = palloc0(sizeof(ScanKey *) * bdesc->bd_tupdesc->natts);
nkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts);
+ nnullkeys = palloc0(sizeof(int) * bdesc->bd_tupdesc->natts);
/*
* Preprocess the scan keys - split them into per-attribute arrays.
@@ -439,23 +423,24 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
TupleDescAttr(bdesc->bd_tupdesc,
keyattno - 1)->attcollation));
- /* First time we see this index attribute, so init as needed. */
- if (!keys[keyattno-1])
+ /*
+ * First time we see this index attribute, so init as needed.
+ *
+ * This is a bit of an overkill - we don't know how many scan
+ * keys are there for a given attribute, so we simply allocate
+ * the largest number possible (as if all scan keys belonged to
+ * the same 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 it's probably cheaper than having
+ * to repalloc repeatedly.
+ */
+ if (consistentFn[keyattno - 1].fn_oid == InvalidOid)
{
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. This may waste
- * a bit of memory, but we only expect small number of
- * scan keys in general, so this should be negligible,
- * and it's cheaper than having to repalloc repeatedly.
- */
- keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);
-
- /* First time this column, so look up consistent function */
- Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid);
+ /* No key/null arrays for this attribute. */
+ Assert((keys[keyattno - 1] == NULL) && (nkeys[keyattno - 1] == 0));
+ Assert((nullkeys[keyattno - 1] == NULL) && (nnullkeys[keyattno - 1] == 0));
tmp = index_getprocinfo(idxRel, keyattno,
BRIN_PROCNUM_CONSISTENT);
@@ -463,9 +448,23 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
CurrentMemoryContext);
}
- /* Add key to the per-attribute array. */
- keys[keyattno - 1][nkeys[keyattno - 1]] = key;
- nkeys[keyattno - 1]++;
+ /* Add key to the proper per-attribute array. */
+ if (key->sk_flags & SK_ISNULL)
+ {
+ if (!nullkeys[keyattno - 1])
+ nullkeys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);
+
+ nullkeys[keyattno - 1][nnullkeys[keyattno - 1]] = key;
+ nnullkeys[keyattno - 1]++;
+ }
+ else
+ {
+ if (!keys[keyattno - 1])
+ keys[keyattno - 1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);
+
+ keys[keyattno - 1][nkeys[keyattno - 1]] = key;
+ nkeys[keyattno - 1]++;
+ }
}
/* allocate an initial in-memory tuple, out of the per-range memcxt */
@@ -552,6 +551,45 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Assert((nkeys[attno - 1] > 0) &&
(nkeys[attno - 1] <= scan->numberOfKeys));
+ /*
+ * First check if there are any IS [NOT] NULL scan keys,
+ * and if we're violating them. In that case we can
+ * terminate early, without invoking the support function.
+ *
+ * As there may be more keys, we can only detemine mismatch
+ * within this loop.
+ */
+ if (bdesc->bd_info[attno - 1]->oi_regular_nulls &&
+ !check_null_keys(bval, nullkeys[attno - 1],
+ nnullkeys[attno - 1]))
+ {
+ /*
+ * If any of the IS [NOT] NULL keys failed, the page
+ * range as a whole can't pass. So terminate the loop.
+ */
+ addrange = false;
+ break;
+ }
+
+ /*
+ * So either there are no IS [NOT] NULL keys, or all passed.
+ * If there are no regular scan keys, we're done - the page
+ * range matches. If there are regular keys, but the page
+ * range is marked as 'all nulls' it can't possibly pass
+ * (we're assuming the operators are strict).
+ */
+
+ /* No regular scan keys - page range as a whole passes. */
+ if (!nkeys[attno - 1])
+ continue;
+
+ /* If it is all nulls, it cannot possibly be consistent. */
+ if (bval->bv_allnulls)
+ {
+ addrange = false;
+ break;
+ }
+
/*
* Check whether the scan key is consistent with the page
* range values; if so, have the pages in the range added
@@ -694,7 +732,6 @@ brinbuildCallback(Relation index,
{
BrinBuildState *state = (BrinBuildState *) brstate;
BlockNumber thisblock;
- int i;
thisblock = ItemPointerGetBlockNumber(tid);
@@ -723,25 +760,8 @@ brinbuildCallback(Relation index,
}
/* Accumulate the current tuple into the running state */
- for (i = 0; i < state->bs_bdesc->bd_tupdesc->natts; i++)
- {
- FmgrInfo *addValue;
- BrinValues *col;
- Form_pg_attribute attr = TupleDescAttr(state->bs_bdesc->bd_tupdesc, i);
-
- col = &state->bs_dtuple->bt_columns[i];
- addValue = index_getprocinfo(index, i + 1,
- BRIN_PROCNUM_ADDVALUE);
-
- /*
- * Update dtuple state, if and as necessary.
- */
- FunctionCall4Coll(addValue,
- attr->attcollation,
- PointerGetDatum(state->bs_bdesc),
- PointerGetDatum(col),
- values[i], isnull[i]);
- }
+ (void) add_values_to_range(index, state->bs_bdesc, state->bs_dtuple,
+ values, isnull);
}
/*
@@ -1520,6 +1540,39 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b)
FmgrInfo *unionFn;
BrinValues *col_a = &a->bt_columns[keyno];
BrinValues *col_b = &db->bt_columns[keyno];
+ BrinOpcInfo *opcinfo = bdesc->bd_info[keyno];
+
+ if (opcinfo->oi_regular_nulls)
+ {
+ /* Adjust "hasnulls". */
+ if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
+ col_a->bv_hasnulls = true;
+
+ /* If there are no values in B, there's nothing left to do. */
+ if (col_b->bv_allnulls)
+ continue;
+
+ /*
+ * Adjust "allnulls". If A doesn't have values, just copy the
+ * values from B into A, and we're done. We cannot run the
+ * operators in this case, because values in A might contain
+ * garbage. Note we already established that B contains values.
+ */
+ if (col_a->bv_allnulls)
+ {
+ int i;
+
+ col_a->bv_allnulls = false;
+
+ for (i = 0; i < opcinfo->oi_nstored; i++)
+ col_a->bv_values[i] =
+ datumCopy(col_b->bv_values[i],
+ opcinfo->oi_typcache[i]->typbyval,
+ opcinfo->oi_typcache[i]->typlen);
+
+ continue;
+ }
+ }
unionFn = index_getprocinfo(bdesc->bd_index, keyno + 1,
BRIN_PROCNUM_UNION);
@@ -1573,3 +1626,103 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
*/
FreeSpaceMapVacuum(idxrel);
}
+
+static bool
+add_values_to_range(Relation idxRel, BrinDesc *bdesc, BrinMemTuple *dtup,
+ Datum *values, bool *nulls)
+{
+ int keyno;
+ bool modified = false;
+
+ /*
+ * Compare the key values of the new tuple to the stored index values;
+ * our deformed tuple will get updated if the new tuple doesn't fit
+ * the original range (note this means we can't break out of the loop
+ * early). Make a note of whether this happens, so that we know to
+ * insert the modified tuple later.
+ */
+ for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++)
+ {
+ Datum result;
+ BrinValues *bval;
+ FmgrInfo *addValue;
+
+ bval = &dtup->bt_columns[keyno];
+
+ if (bdesc->bd_info[keyno]->oi_regular_nulls && nulls[keyno])
+ {
+ /*
+ * If the new value is null, we record that we saw it if it's
+ * the first one; otherwise, there's nothing to do.
+ */
+ if (!bval->bv_hasnulls)
+ {
+ bval->bv_hasnulls = true;
+ modified = true;
+ }
+
+ continue;
+ }
+
+ addValue = index_getprocinfo(idxRel, keyno + 1,
+ BRIN_PROCNUM_ADDVALUE);
+ result = FunctionCall4Coll(addValue,
+ idxRel->rd_indcollation[keyno],
+ PointerGetDatum(bdesc),
+ PointerGetDatum(bval),
+ values[keyno],
+ nulls[keyno]);
+ /* if that returned true, we need to insert the updated tuple */
+ modified |= DatumGetBool(result);
+ }
+
+ return modified;
+}
+
+static bool
+check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys)
+{
+ int keyno;
+
+ /*
+ * First check if there are any IS [NOT] NULL scan keys, and if we're
+ * violating them.
+ */
+ for (keyno = 0; keyno < nnullkeys; keyno++)
+ {
+ ScanKey key = nullkeys[keyno];
+
+ Assert(key->sk_attno == bval->bv_attno);
+
+ /* Handle only IS NULL/IS NOT NULL tests */
+ if (!(key->sk_flags & SK_ISNULL))
+ continue;
+
+ if (key->sk_flags & SK_SEARCHNULL)
+ {
+ /* IS NULL scan key, but range has no NULLs */
+ if (!bval->bv_allnulls && !bval->bv_hasnulls)
+ return false;
+ }
+ else if (key->sk_flags & SK_SEARCHNOTNULL)
+ {
+ /*
+ * For IS NOT NULL, we can only skip ranges that are known to
+ * have only nulls.
+ */
+ if (bval->bv_allnulls)
+ return false;
+ }
+ else
+ {
+ /*
+ * Neither IS NULL nor IS NOT NULL was used; assume all indexable
+ * operators are strict and thus return false with NULL value in
+ * the scan key.
+ */
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c
index 853727190c..f5eaef416f 100644
--- a/src/backend/access/brin/brin_inclusion.c
+++ b/src/backend/access/brin/brin_inclusion.c
@@ -110,6 +110,7 @@ brin_inclusion_opcinfo(PG_FUNCTION_ARGS)
*/
result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque));
result->oi_nstored = 3;
+ result->oi_regular_nulls = true;
result->oi_opaque = (InclusionOpaque *)
MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
@@ -141,7 +142,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
Datum newval = PG_GETARG_DATUM(2);
- bool isnull = PG_GETARG_BOOL(3);
+ bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3);
Oid colloid = PG_GET_COLLATION();
FmgrInfo *finfo;
Datum result;
@@ -149,18 +150,7 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
AttrNumber attno;
Form_pg_attribute attr;
- /*
- * If the new value is null, we record that we saw it if it's the first
- * one; otherwise, there's nothing to do.
- */
- if (isnull)
- {
- if (column->bv_hasnulls)
- PG_RETURN_BOOL(false);
-
- column->bv_hasnulls = true;
- PG_RETURN_BOOL(true);
- }
+ Assert(!isnull);
attno = column->bv_attno;
attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
@@ -264,63 +254,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
int nkeys = PG_GETARG_INT32(3);
Oid colloid = PG_GET_COLLATION();
int keyno;
- bool regular_keys = false;
-
- /*
- * First check if there are any IS NULL scan keys, and if we're
- * violating them. In that case we can terminate early, without
- * inspecting the ranges.
- */
- for (keyno = 0; keyno < nkeys; keyno++)
- {
- ScanKey key = keys[keyno];
-
- Assert(key->sk_attno == column->bv_attno);
-
- /* handle IS NULL/IS NOT NULL tests */
- if (key->sk_flags & SK_ISNULL)
- {
- if (key->sk_flags & SK_SEARCHNULL)
- {
- if (column->bv_allnulls || column->bv_hasnulls)
- continue; /* this key is fine, continue */
-
- PG_RETURN_BOOL(false);
- }
-
- /*
- * For IS NOT NULL, we can only skip ranges that are known to have
- * only nulls.
- */
- if (key->sk_flags & SK_SEARCHNOTNULL)
- {
- if (column->bv_allnulls)
- PG_RETURN_BOOL(false);
-
- continue;
- }
-
- /*
- * Neither IS NULL nor IS NOT NULL was used; assume all indexable
- * operators are strict and return false.
- */
- PG_RETURN_BOOL(false);
- }
- else
- /* note we have regular (non-NULL) scan keys */
- regular_keys = true;
- }
-
- /*
- * If the page range is all nulls, it cannot possibly be consistent if
- * there are some regular scan keys.
- */
- if (column->bv_allnulls && regular_keys)
- PG_RETURN_BOOL(false);
-
- /* If there are no regular keys, the page range is considered consistent. */
- if (!regular_keys)
- PG_RETURN_BOOL(true);
/* It has to be checked, if it contains elements that are not mergeable. */
if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
@@ -331,9 +264,8 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
{
ScanKey key = keys[keyno];
- /* ignore IS NULL/IS NOT NULL tests handled above */
- if (key->sk_flags & SK_ISNULL)
- continue;
+ /* 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
@@ -574,37 +506,11 @@ brin_inclusion_union(PG_FUNCTION_ARGS)
Datum result;
Assert(col_a->bv_attno == col_b->bv_attno);
-
- /* Adjust "hasnulls". */
- if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
- col_a->bv_hasnulls = true;
-
- /* If there are no values in B, there's nothing left to do. */
- if (col_b->bv_allnulls)
- PG_RETURN_VOID();
+ Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
attno = col_a->bv_attno;
attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
- /*
- * Adjust "allnulls". If A doesn't have values, just copy the values from
- * B into A, and we're done. We cannot run the operators in this case,
- * because values in A might contain garbage. Note we already established
- * that B contains values.
- */
- if (col_a->bv_allnulls)
- {
- col_a->bv_allnulls = false;
- col_a->bv_values[INCLUSION_UNION] =
- datumCopy(col_b->bv_values[INCLUSION_UNION],
- attr->attbyval, attr->attlen);
- col_a->bv_values[INCLUSION_UNMERGEABLE] =
- col_b->bv_values[INCLUSION_UNMERGEABLE];
- col_a->bv_values[INCLUSION_CONTAINS_EMPTY] =
- col_b->bv_values[INCLUSION_CONTAINS_EMPTY];
- PG_RETURN_VOID();
- }
-
/* If B includes empty elements, mark A similarly, if needed. */
if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&
DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY]))
diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c
index a3bb3558ec..a625ef7aac 100644
--- a/src/backend/access/brin/brin_minmax.c
+++ b/src/backend/access/brin/brin_minmax.c
@@ -48,6 +48,7 @@ brin_minmax_opcinfo(PG_FUNCTION_ARGS)
result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
sizeof(MinmaxOpaque));
result->oi_nstored = 2;
+ result->oi_regular_nulls = true;
result->oi_opaque = (MinmaxOpaque *)
MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
result->oi_typcache[0] = result->oi_typcache[1] =
@@ -69,7 +70,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS)
BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
Datum newval = PG_GETARG_DATUM(2);
- bool isnull = PG_GETARG_DATUM(3);
+ bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3);
Oid colloid = PG_GET_COLLATION();
FmgrInfo *cmpFn;
Datum compar;
@@ -77,18 +78,7 @@ brin_minmax_add_value(PG_FUNCTION_ARGS)
Form_pg_attribute attr;
AttrNumber attno;
- /*
- * If the new value is null, we record that we saw it if it's the first
- * one; otherwise, there's nothing to do.
- */
- if (isnull)
- {
- if (column->bv_hasnulls)
- PG_RETURN_BOOL(false);
-
- column->bv_hasnulls = true;
- PG_RETURN_BOOL(true);
- }
+ Assert(!isnull);
attno = column->bv_attno;
attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
@@ -152,72 +142,14 @@ brin_minmax_consistent(PG_FUNCTION_ARGS)
int nkeys = PG_GETARG_INT32(3);
Oid colloid = PG_GET_COLLATION();
int keyno;
- bool regular_keys = false;
-
- /*
- * First check if there are any IS NULL scan keys, and if we're
- * violating them. In that case we can terminate early, without
- * inspecting the ranges.
- */
- for (keyno = 0; keyno < nkeys; keyno++)
- {
- ScanKey key = keys[keyno];
-
- Assert(key->sk_attno == column->bv_attno);
-
- /* handle IS NULL/IS NOT NULL tests */
- if (key->sk_flags & SK_ISNULL)
- {
- if (key->sk_flags & SK_SEARCHNULL)
- {
- if (column->bv_allnulls || column->bv_hasnulls)
- continue; /* this key is fine, continue */
-
- PG_RETURN_BOOL(false);
- }
-
- /*
- * For IS NOT NULL, we can only skip ranges that are known to have
- * only nulls.
- */
- if (key->sk_flags & SK_SEARCHNOTNULL)
- {
- if (column->bv_allnulls)
- PG_RETURN_BOOL(false);
-
- continue;
- }
-
- /*
- * Neither IS NULL nor IS NOT NULL was used; assume all indexable
- * operators are strict and return false.
- */
- PG_RETURN_BOOL(false);
- }
- else
- /* note we have regular (non-NULL) scan keys */
- regular_keys = true;
- }
-
- /*
- * If the page range is all nulls, it cannot possibly be consistent if
- * there are some regular scan keys.
- */
- if (column->bv_allnulls && regular_keys)
- PG_RETURN_BOOL(false);
-
- /* If there are no regular keys, the page range is considered consistent. */
- if (!regular_keys)
- PG_RETURN_BOOL(true);
/* 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;
+ /* 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
@@ -308,34 +240,11 @@ brin_minmax_union(PG_FUNCTION_ARGS)
bool needsadj;
Assert(col_a->bv_attno == col_b->bv_attno);
-
- /* Adjust "hasnulls" */
- if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
- col_a->bv_hasnulls = true;
-
- /* If there are no values in B, there's nothing left to do */
- if (col_b->bv_allnulls)
- PG_RETURN_VOID();
+ Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
attno = col_a->bv_attno;
attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
- /*
- * Adjust "allnulls". If A doesn't have values, just copy the values from
- * B into A, and we're done. We cannot run the operators in this case,
- * because values in A might contain garbage. Note we already established
- * that B contains values.
- */
- if (col_a->bv_allnulls)
- {
- col_a->bv_allnulls = false;
- col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
- attr->attbyval, attr->attlen);
- col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
- attr->attbyval, attr->attlen);
- PG_RETURN_VOID();
- }
-
/* Adjust minimum, if B's min is less than A's min */
finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 9ffc9100c0..7c4f3da0a0 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -27,6 +27,9 @@ typedef struct BrinOpcInfo
/* Number of columns stored in an index column of this opclass */
uint16 oi_nstored;
+ /* Regular processing of NULLs in BrinValues? */
+ bool oi_regular_nulls;
+
/* Opaque pointer for the opclass' private use */
void *oi_opaque;
--
2.25.4
[text/plain] 0003-BRIN-bloom-indexes-20200912.patch (138.7K, ../20200912161303.z54jctx5dw4srrh3@development/4-0003-BRIN-bloom-indexes-20200912.patch)
download | inline diff:
From 2839692af136e25083b00cba8c98f42411b05098 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 12 Sep 2020 15:07:43 +0200
Subject: [PATCH 3/5] BRIN bloom indexes
Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN
indexes using the new opclasses only allow equality queries, but that
works for data like UUID, MAC addresses etc. for which range queries are
not very common (and the data look random, making BRIN minmax indexes
inefficient anyway).
BRIN bloom opclasses allow specifying the usual Bloom parameters, like
expected number of distinct values (in the BRIN range) and desired
false positive rate.
The opclasses store 32-bit hashes of the values, not the raw indexed
values. This assumes the hash functions for data types has low number
of collisions, good performance etc. Collisions are not a huge issue
though, because the number of values in a BRIN ranges is fairly small.
The summary does not start as a Bloom filter right away - it initially
stores a plain array of hashes. Only when the size of the array grows
too large it switches to proper Bloom filter. This means that ranges
with low number of distinct values the summary will use less space.
Author: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com
---
doc/src/sgml/brin.sgml | 178 ++++
doc/src/sgml/ref/create_index.sgml | 31 +
src/backend/access/brin/Makefile | 1 +
src/backend/access/brin/brin_bloom.c | 1107 +++++++++++++++++++++
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 4 +
src/include/catalog/pg_amop.dat | 170 ++++
src/include/catalog/pg_amproc.dat | 447 +++++++++
src/include/catalog/pg_opclass.dat | 72 ++
src/include/catalog/pg_opfamily.dat | 38 +
src/include/catalog/pg_proc.dat | 34 +
src/include/catalog/pg_type.dat | 7 +
src/test/regress/expected/brin_bloom.out | 456 +++++++++
src/test/regress/expected/opr_sanity.out | 3 +-
src/test/regress/expected/psql.out | 3 +-
src/test/regress/expected/type_sanity.out | 7 +-
src/test/regress/parallel_schedule | 5 +
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/brin_bloom.sql | 404 ++++++++
19 files changed, 2965 insertions(+), 5 deletions(-)
create mode 100644 src/backend/access/brin/brin_bloom.c
create mode 100644 src/test/regress/expected/brin_bloom.out
create mode 100644 src/test/regress/sql/brin_bloom.sql
diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml
index 4420794e5b..577dd18c49 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -128,6 +128,10 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
</row>
</thead>
<tbody>
+ <row>
+ <entry valign="middle"><literal>int8_bloom_ops</literal></entry>
+ <entry><literal>= (bit,bit)</literal></entry>
+ </row>
<row>
<entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry>
<entry><literal>= (bit,bit)</literal></entry>
@@ -154,6 +158,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>|&> (box,box)</literal></entry></row>
<row><entry><literal>|>> (box,box)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry>
+ <entry><literal>= (character,character)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry>
<entry><literal>= (character,character)</literal></entry>
@@ -163,6 +172,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (character,character)</literal></entry></row>
<row><entry><literal>>= (character,character)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>bytea_bloom_ops</literal></entry>
+ <entry><literal>= (bytea,bytea)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry>
<entry><literal>= (bytea,bytea)</literal></entry>
@@ -172,6 +186,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (bytea,bytea)</literal></entry></row>
<row><entry><literal>>= (bytea,bytea)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>char_bloom_ops</literal></entry>
+ <entry><literal>= ("char","char")</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry>
<entry><literal>= ("char","char")</literal></entry>
@@ -181,6 +200,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> ("char","char")</literal></entry></row>
<row><entry><literal>>= ("char","char")</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>date_bloom_ops</literal></entry>
+ <entry><literal>= (date,date)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry>
<entry><literal>= (date,date)</literal></entry>
@@ -190,6 +214,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (date,date)</literal></entry></row>
<row><entry><literal>>= (date,date)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>float4_bloom_ops</literal></entry>
+ <entry><literal>= (float4,float4)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry>
<entry><literal>= (float4,float4)</literal></entry>
@@ -199,6 +228,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (float4,float4)</literal></entry></row>
<row><entry><literal>>= (float4,float4)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>float8_bloom_ops</literal></entry>
+ <entry><literal>= (float8,float8)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry>
<entry><literal>= (float8,float8)</literal></entry>
@@ -218,6 +252,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>= (inet,inet)</literal></entry></row>
<row><entry><literal>&& (inet,inet)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>inet_bloom_ops</literal></entry>
+ <entry><literal>= (inet,inet)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry>
<entry><literal>= (inet,inet)</literal></entry>
@@ -227,6 +266,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (inet,inet)</literal></entry></row>
<row><entry><literal>>= (inet,inet)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>int2_bloom_ops</literal></entry>
+ <entry><literal>= (int2,int2)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry>
<entry><literal>= (int2,int2)</literal></entry>
@@ -236,6 +280,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (int2,int2)</literal></entry></row>
<row><entry><literal>>= (int2,int2)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>int4_bloom_ops</literal></entry>
+ <entry><literal>= (int4,int4)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry>
<entry><literal>= (int4,int4)</literal></entry>
@@ -245,6 +294,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (int4,int4)</literal></entry></row>
<row><entry><literal>>= (int4,int4)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>int8_bloom_ops</literal></entry>
+ <entry><literal>= (bigint,bigint)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry>
<entry><literal>= (bigint,bigint)</literal></entry>
@@ -254,6 +308,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (bigint,bigint)</literal></entry></row>
<row><entry><literal>>= (bigint,bigint)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>interval_bloom_ops</literal></entry>
+ <entry><literal>= (interval,interval)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry>
<entry><literal>= (interval,interval)</literal></entry>
@@ -263,6 +322,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (interval,interval)</literal></entry></row>
<row><entry><literal>>= (interval,interval)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry>
+ <entry><literal>= (macaddr,macaddr)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry>
<entry><literal>= (macaddr,macaddr)</literal></entry>
@@ -272,6 +336,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (macaddr,macaddr)</literal></entry></row>
<row><entry><literal>>= (macaddr,macaddr)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry>
+ <entry><literal>= (macaddr8,macaddr8)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry>
<entry><literal>= (macaddr8,macaddr8)</literal></entry>
@@ -281,6 +350,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (macaddr8,macaddr8)</literal></entry></row>
<row><entry><literal>>= (macaddr8,macaddr8)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>name_bloom_ops</literal></entry>
+ <entry><literal>= (name,name)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry>
<entry><literal>= (name,name)</literal></entry>
@@ -290,6 +364,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (name,name)</literal></entry></row>
<row><entry><literal>>= (name,name)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>numeric_bloom_ops</literal></entry>
+ <entry><literal>= (numeric,numeric)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry>
<entry><literal>= (numeric,numeric)</literal></entry>
@@ -299,6 +378,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (numeric,numeric)</literal></entry></row>
<row><entry><literal>>= (numeric,numeric)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>oid_bloom_ops</literal></entry>
+ <entry><literal>= (oid,oid)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry>
<entry><literal>= (oid,oid)</literal></entry>
@@ -308,6 +392,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (oid,oid)</literal></entry></row>
<row><entry><literal>>= (oid,oid)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry>
+ <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry>
<entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
@@ -335,6 +424,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>&> (anyrange,anyrange)</literal></entry></row>
<row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>text_bloom_ops</literal></entry>
+ <entry><literal>= (text,text)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry>
<entry><literal>= (text,text)</literal></entry>
@@ -344,6 +438,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (text,text)</literal></entry></row>
<row><entry><literal>>= (text,text)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>tid_bloom_ops</literal></entry>
+ <entry><literal>= (tid,tid)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry>
<entry><literal>= (tid,tid)</literal></entry>
@@ -353,6 +452,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (tid,tid)</literal></entry></row>
<row><entry><literal>>= (tid,tid)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry>
+ <entry><literal>= (timestamp,timestamp)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry>
<entry><literal>= (timestamp,timestamp)</literal></entry>
@@ -362,6 +466,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timestamp,timestamp)</literal></entry></row>
<row><entry><literal>>= (timestamp,timestamp)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry>
+ <entry><literal>= (timestamptz,timestamptz)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry>
<entry><literal>= (timestamptz,timestamptz)</literal></entry>
@@ -371,6 +480,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timestamptz,timestamptz)</literal></entry></row>
<row><entry><literal>>= (timestamptz,timestamptz)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>time_bloom_ops</literal></entry>
+ <entry><literal>= (time,time)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry>
<entry><literal>= (time,time)</literal></entry>
@@ -380,6 +494,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (time,time)</literal></entry></row>
<row><entry><literal>>= (time,time)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>timetz_bloom_ops</literal></entry>
+ <entry><literal>= (timetz,timetz)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry>
<entry><literal>= (timetz,timetz)</literal></entry>
@@ -389,6 +508,11 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timetz,timetz)</literal></entry></row>
<row><entry><literal>>= (timetz,timetz)</literal></entry></row>
+ <row>
+ <entry valign="middle"><literal>uuid_bloom_ops</literal></entry>
+ <entry><literal>= (uuid,uuid)</literal></entry>
+ </row>
+
<row>
<entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry>
<entry><literal>= (uuid,uuid)</literal></entry>
@@ -779,6 +903,60 @@ typedef struct BrinOpcInfo
function can improve index performance.
</para>
+ <para>
+ To write an operator class for a data type that implements only an equality
+ operator and supports hashing, it is possible to use the bloom support procedures
+ alongside the corresponding operators, as shown in
+ <xref linkend="brin-extensibility-bloom-table"/>.
+ All operator class members (procedures and operators) are mandatory.
+ </para>
+
+ <table id="brin-extensibility-bloom-table">
+ <title>Procedure and Support Numbers for Bloom Operator Classes</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Operator class member</entry>
+ <entry>Object</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Support Procedure 1</entry>
+ <entry>internal function <function>brin_bloom_opcinfo()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 2</entry>
+ <entry>internal function <function>brin_bloom_add_value()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 3</entry>
+ <entry>internal function <function>brin_bloom_consistent()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 4</entry>
+ <entry>internal function <function>brin_bloom_union()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 11</entry>
+ <entry>function to compute hash of an element</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 1</entry>
+ <entry>operator equal-to</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Support procedure numbers 1-10 are reserved for the BRIN internal
+ functions, so the SQL level functions start with number 11. Support
+ function number 11 is the main function required to build the index.
+ It should accept one argument with the same data type as the operator class,
+ and return a hash of the value.
+ </para>
+
<para>
Both minmax and inclusion operator classes support cross-data-type
operators, though with these the dependencies become more complicated.
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 33aa64e81d..9c90d451a7 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -555,6 +555,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>n_distinct_per_range</literal></term>
+ <listitem>
+ <para>
+ Defines the estimated number of distinct non-null values in the block
+ range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the
+ Bloom filter. It behaves similarly to <literal>n_distinct</literal> option
+ for <xref linkend="sql-altertable"/>. When set to a positive value,
+ each block range is assumed to contain this number of distinct non-null
+ values. When set to a negative value, which must be greater than or
+ equal to -1, the number of distinct non-null is assumed linear with
+ the maximum possible number of tuples in the block range (about 290
+ rows per block). The default values is <literal>-0.1</literal>, and
+ the minimum number of distinct non-null values is <literal>128</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>false_positive_rate</literal></term>
+ <listitem>
+ <para>
+ Defines the desired false positive rate used by <acronym>BRIN</acronym>
+ bloom indexes for sizing of the Bloom filter. The values must be be
+ greater than 0.0 and smaller than 1.0. The default values is 0.01,
+ which is 1% false positive rate.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect2>
diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile
index 468e1e289a..6b56131215 100644
--- a/src/backend/access/brin/Makefile
+++ b/src/backend/access/brin/Makefile
@@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = \
brin.o \
+ brin_bloom.o \
brin_inclusion.o \
brin_minmax.o \
brin_pageops.o \
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
new file mode 100644
index 0000000000..c2cbbd9400
--- /dev/null
+++ b/src/backend/access/brin/brin_bloom.c
@@ -0,0 +1,1107 @@
+/*
+ * brin_bloom.c
+ * Implementation of Bloom opclass for BRIN
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * A BRIN opclass summarizing page range into a bloom filter.
+ *
+ * Bloom filters allow efficient test whether a given page range contains
+ * a particular value. Therefore, if we summarize each page range into a
+ * bloom filter, we can easily and cheaply test wheter it containst values
+ * we get later.
+ *
+ * The index only supports equality operator, similarly to hash indexes.
+ * BRIN bloom indexes are however much smaller, and support only bitmap
+ * scans.
+ *
+ * Note: Don't confuse this with bloom indexes, implemented in a contrib
+ * module. That extension implements an entirely new AM, building a bloom
+ * filter on multiple columns in a single row. This opclass works with an
+ * existing AM (BRIN) and builds bloom filter on a column.
+ *
+ *
+ * values vs. hashes
+ * -----------------
+ *
+ * The original column values are not used directly, but are first hashed
+ * using the regular type-specific hash function, producing a uint32 hash.
+ * And this hash value is then added to the summary - either stored as is
+ * (in the sorted mode), or hashed again and added to the bloom filter.
+ *
+ * This allows the code to treat all data types (byval/byref/...) the same
+ * way, with only minimal space requirements. For example we don't need to
+ * store varlena types differently in the sorted mode, etc. Everything is
+ * uint32 making it much simpler.
+ *
+ * Of course, this assumes the built-in hash function is reasonably good,
+ * without too many collisions etc. But that does seem to be the case, at
+ * least based on past experience. After all, the same hash functions are
+ * used for hash indexes, hash partitioning and so on.
+ *
+ *
+ * sizing the bloom filter
+ * -----------------------
+ *
+ * Size of a bloom filter depends on the number of distinct values we will
+ * store in it, and the desired false positive rate. The higher the number
+ * of distinct values and/or the lower the false positive rate, the larger
+ * the bloom filter. On the other hand, we want to keep the index as small
+ * as possible - that's one of the basic advantages of BRIN indexes.
+ *
+ * The number of distinct elements (in a page range) depends on the data,
+ * we can consider it fixed. This simplifies the trade-off to just false
+ * positive rate vs. size.
+ *
+ * At the page range level, false positive rate is a probability the bloom
+ * filter matches a random value. For the whole index (with sufficiently
+ * many page ranges) it represents the fraction of the index ranges (and
+ * thus fraction of the table to be scanned) matching the random value.
+ *
+ * Furthermore, the size of the bloom filter is subject to implementation
+ * limits - it has to fit onto a single index page (8kB by default). As
+ * the bitmap is inherently random, compression can't reliably help here.
+ * To reduce the size of a filter (to fit to a page), we have to either
+ * accept higher false positive rate (undesirable), or reduce the number
+ * of distinct items to be stored in the filter. We can't quite the input
+ * data, of course, but we may make the BRIN page ranges smaller - instead
+ * of the default 128 pages (1MB) we may build index with 16-page ranges,
+ * or something like that. This does help even for random data sets, as
+ * the number of rows per heap page is limited (to ~290 with very narrow
+ * tables, likely ~20 in practice).
+ *
+ * Of course, good sizing decisions depend on having the necessary data,
+ * i.e. number of distinct values in a page range (of a given size) and
+ * table size (to estimate cost change due to change in false positive
+ * rate due to having larger index vs. scanning larger indexes). We may
+ * not have that data - for example when building an index on empty table
+ * it's not really possible. And for some data we only have estimates for
+ * the whole table and we can only estimate per-range values (ndistinct).
+ *
+ * Another challenge is that while the bloom filter is per-column, it's
+ * the whole index tuple that has to fit into a page. And for multi-column
+ * indexes that may include pieces we have no control over (not necessarily
+ * bloom filters, the other columns may use other BRIN opclasses). So it's
+ * not entirely clear how to distrubute the space between those columns.
+ *
+ * The current logic, implemented in brin_bloom_get_ndistinct, attempts to
+ * make some basic sizing decisions, based on the table ndistinct estimate.
+ *
+ *
+ * sort vs. hash
+ * -------------
+ *
+ * As explained in the preceding section, sizing bloom filters correctly is
+ * difficult in practice. It's also true that bloom filters quickly degrade
+ * after exceeding the expected number of distinct items. It's therefore
+ * expected that people will overshoot the parameters a bit (particularly
+ * the ndistinct per page range), perhaps by a factor of 2 or more.
+ *
+ * It's also possible the data set is not uniform - it may have ranges with
+ * very many distinct items per range, but also ranges with only very few
+ * distinct items. The bloom filter has to be sized for the more variable
+ * ranges, making it rather wasteful in the less variable part.
+ *
+ * For example, if some page ranges have 1000 distinct values, that means
+ * about ~1.2kB bloom filter with 1% false positive rate. For page ranges
+ * that only contain 10 distinct values, that's wasteful, because we might
+ * store the values (the uint32 hashes) in just 40B.
+ *
+ * To address these issues, the opclass stores the raw values directly, and
+ * only switches to the actual bloom filter after reaching the same space
+ * requirements.
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/brin/brin_bloom.c
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/brin.h"
+#include "access/brin_internal.h"
+#include "access/brin_tuple.h"
+#include "access/hash.h"
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/stratnum.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_amop.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+#include <math.h>
+
+#define BloomEqualStrategyNumber 1
+
+/*
+ * Additional SQL level support functions
+ *
+ * Procedure numbers must not use values reserved for BRIN itself; see
+ * brin_internal.h.
+ */
+#define BLOOM_MAX_PROCNUMS 1 /* maximum support procs we need */
+#define PROCNUM_HASH 11 /* required */
+
+/*
+ * Subtract this from procnum to obtain index in BloomOpaque arrays
+ * (Must be equal to minimum of private procnums).
+ */
+#define PROCNUM_BASE 11
+
+/*
+ * Flags. We only use a single bit for now, to decide whether we're in the
+ * sorted or hash phase. So we have 15 bits for future use, if needed. The
+ * filters are expected to be hundreds of bytes, so this is negligible.
+ */
+#define BLOOM_FLAG_PHASE_HASH 0x0001
+
+#define BLOOM_IS_HASHED(f) ((f)->flags & BLOOM_FLAG_PHASE_HASH)
+#define BLOOM_IS_SORTED(f) (!BLOOM_IS_HASHED(f))
+
+/*
+ * Number of hashes to accumulate before deduplicating and sorting in the
+ * sort phase? We want this fairly small to reduce the amount of space and
+ * also speed-up bsearch lookups.
+ */
+#define BLOOM_MAX_UNSORTED 32
+
+/*
+ * Storage type for BRIN's reloptions.
+ */
+typedef struct BloomOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ double nDistinctPerRange; /* number of distinct values per range */
+ double falsePositiveRate; /* false positive for bloom filter */
+} BloomOptions;
+
+/*
+ * The current min value (16) is somewhat arbitrary, but it's based
+ * on the fact that the filter header is ~20B alone, which is about
+ * the same as the filter bitmap for 16 distinct items with 1% false
+ * positive rate. So by allowing lower values we'd not gain much. In
+ * any case, the min should not be larger than MaxHeapTuplesPerPage
+ * (~290), which is the theoretical maximum for single-page ranges.
+ */
+#define BLOOM_MIN_NDISTINCT_PER_RANGE 16
+
+/*
+ * Used to determine number of distinct items, based on the number of rows
+ * in a page range. The 10% is somewhat similar to what estimate_num_groups
+ * does, so we use the same factor here.
+ */
+#define BLOOM_DEFAULT_NDISTINCT_PER_RANGE -0.1 /* 10% of values */
+
+/*
+ * The 1% value is mostly arbitrary, it just looks nice.
+ */
+#define BLOOM_DEFAULT_FALSE_POSITIVE_RATE 0.01 /* 1% fp rate */
+
+#define BloomGetNDistinctPerRange(opts) \
+ ((opts) && (((BloomOptions *) (opts))->nDistinctPerRange != 0) ? \
+ (((BloomOptions *) (opts))->nDistinctPerRange) : \
+ BLOOM_DEFAULT_NDISTINCT_PER_RANGE)
+
+#define BloomGetFalsePositiveRate(opts) \
+ ((opts) && (((BloomOptions *) (opts))->falsePositiveRate != 0.0) ? \
+ (((BloomOptions *) (opts))->falsePositiveRate) : \
+ BLOOM_DEFAULT_FALSE_POSITIVE_RATE)
+
+/*
+ * Bloom Filter
+ *
+ * Represents a bloom filter, built on hashes of the indexed values. That is,
+ * we compute a uint32 hash of the value, and then store this hash into the
+ * bloom filter (and compute additional hashes on it).
+ *
+ * We use an optimisation that initially we store the uint32 values directly,
+ * without the extra hashing step. And only later filling the bitmap space,
+ * we switch to the regular bloom filter mode.
+ *
+ * PHASE_SORTED
+ *
+ * Initially we copy the uint32 hash into the bitmap, regularly sorting the
+ * hash values for fast lookup (we keep at most BLOOM_MAX_UNSORTED unsorted
+ * values).
+ *
+ * The idea is that if we only see very few distinct values, we can store
+ * them in less space compared to the (sparse) bloom filter bitmap. It also
+ * stores them exactly, although that's not a big advantage as almost-empty
+ * bloom filter has false positive rate close to zero anyway.
+ *
+ * PHASE_HASH
+ *
+ * Once we fill the bitmap space in the sorted phase, we switch to the hash
+ * phase, where we actually use the bloom filter. We treat the uint32 hashes
+ * as input values, and hash them again with different seeds (to get the k
+ * hash functions needed for bloom filter).
+ *
+ *
+ * XXX Perhaps we could save a few bytes by using different data types, but
+ * considering the size of the bitmap, the difference is negligible.
+ *
+ * XXX We could also implement "sparse" bloom filters, keeping only the
+ * bytes that are not entirely 0. That might make the "sorted" phase
+ * mostly unnecessary.
+ *
+ * XXX We can also watch the number of bits set in the bloom filter, and
+ * then stop using it (and not store the bitmap, to save space) when the
+ * false positive rate gets too high.
+ */
+typedef struct BloomFilter
+{
+ /* varlena header (do not touch directly!) */
+ int32 vl_len_;
+
+ /* space for various flags (phase, etc.) */
+ uint16 flags;
+
+ /* fields used only in the SORTED phase */
+ uint16 nvalues; /* number of hashes stored (total) */
+ uint16 nsorted; /* number of hashes in the sorted part */
+
+ /* fields for the HASHED phase */
+ uint8 nhashes; /* number of hash functions */
+ uint32 nbits; /* number of bits in the bitmap (size) */
+ uint32 nbits_set; /* number of bits set to 1 */
+
+ /* data of the bloom filter (used both for sorted and hashed phase) */
+ char data[FLEXIBLE_ARRAY_MEMBER];
+
+} BloomFilter;
+
+static BloomFilter *bloom_switch_to_hashing(BloomFilter *filter);
+
+
+/*
+ * bloom_init
+ * Initialize the Bloom Filter, allocate all the memory.
+ *
+ * The filter is initialized with optimal size for ndistinct expected
+ * values, and requested false positive rate. The filter is stored as
+ * varlena.
+ */
+static BloomFilter *
+bloom_init(int ndistinct, double false_positive_rate)
+{
+ Size len;
+ BloomFilter *filter;
+
+ int m; /* number of bits */
+ double k; /* number of hash functions */
+
+ Assert(ndistinct > 0);
+ Assert((false_positive_rate > 0) && (false_positive_rate < 1.0));
+
+ m = ceil((ndistinct * log(false_positive_rate)) / log(1.0 / (pow(2.0, log(2.0)))));
+
+ /* round m to whole bytes */
+ m = ((m + 7) / 8) * 8;
+
+ /*
+ * round(log(2.0) * m / ndistinct), but assume round() may not be
+ * available on Windows
+ */
+ k = log(2.0) * m / ndistinct;
+ k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k);
+
+ /*
+ * Allocate the bloom filter with a minimum size 64B (about 40B in the
+ * bitmap part). We require space at least for the header.
+ *
+ * XXX Maybe the 64B min size is not really needed?
+ */
+ len = Max(offsetof(BloomFilter, data), 64);
+
+ filter = (BloomFilter *) palloc0(len);
+
+ filter->flags = 0; /* implies SORTED phase */
+ filter->nhashes = (int) k;
+ filter->nbits = m;
+
+ SET_VARSIZE(filter, len);
+
+ return filter;
+}
+
+/* simple uint32 comparator, for pg_qsort and bsearch */
+static int
+cmp_uint32(const void *a, const void *b)
+{
+ uint32 *ia = (uint32 *) a;
+ uint32 *ib = (uint32 *) b;
+
+ if (*ia == *ib)
+ return 0;
+ else if (*ia < *ib)
+ return -1;
+ else
+ return 1;
+}
+
+/*
+ * bloom_compact
+ * Compact the filter during the 'sorted' phase.
+ *
+ * We sort the uint32 hashes and remove duplicates, for two main reasons.
+ * Firstly, to keep most of the data sorted for bsearch lookups. Secondly,
+ * we try to save space by removing the duplicates, allowing us to stay
+ * in the sorted phase a bit longer.
+ *
+ * We currently don't repalloc the bitmap, i.e. we don't free the memory
+ * here - in the worst case we waste space for up to 32 unsorted hashes
+ * (if all of them are already in the sorted part), so about 128B. We can
+ * either reduce the number of unsorted items (e.g. to 8 hashes, which
+ * would mean 32B), or start doing the repalloc.
+ *
+ * We do however set the varlena length, to minimize the storage needs.
+ */
+static void
+bloom_compact(BloomFilter *filter)
+{
+ int i, j, k;
+ Size len;
+
+ uint32 *values;
+ uint32 *result;
+ uint32 *sorted;
+ uint32 *unsorted;
+
+ int nvalues;
+ int nsorted;
+ int nunsorted;
+
+ /* never call compact on filters in HASH phase */
+ Assert(BLOOM_IS_SORTED(filter));
+
+ /* when already fully sorted, no chance to compact anything */
+ if (filter->nvalues == filter->nsorted)
+ return;
+
+ values = (uint32 *) filter->data;
+
+ /* result of merging */
+ k = 0;
+ result = (uint32 *) palloc(sizeof(uint32) * filter->nvalues);
+
+ /* first we have sorted data, then unsorted */
+ sorted = values;
+ nsorted = filter->nsorted;
+
+ unsorted = &values[filter->nsorted];
+ nunsorted = filter->nvalues - filter->nsorted;
+
+ /* sort the unsorted part, then merge the two parts */
+ pg_qsort(unsorted, nunsorted, sizeof(uint32), cmp_uint32);
+
+ i = 0; /* sorted index */
+ j = 0; /* unsorted index */
+
+ while ((i < nsorted) && (j < nunsorted))
+ {
+ if (sorted[i] <= unsorted[j])
+ result[k++] = sorted[i++];
+ else
+ result[k++] = unsorted[j++];
+ }
+
+ while (i < nsorted)
+ result[k++] = sorted[i++];
+
+ while (j < nunsorted)
+ result[k++] = unsorted[j++];
+
+ /* compact - remove duplicate values */
+ nvalues = 1;
+ for (i = 1; i < filter->nvalues; i++)
+ {
+ /* if different from the last value, keep it */
+ if (result[i] != result[nvalues - 1])
+ result[nvalues++] = result[i];
+ }
+
+ memcpy(filter->data, result, sizeof(uint32) * nvalues);
+
+ filter->nvalues = nvalues;
+ filter->nsorted = nvalues;
+
+ len = offsetof(BloomFilter, data) +
+ (filter->nvalues) * sizeof(uint32);
+
+ SET_VARSIZE(filter, len);
+}
+
+/*
+ * bloom_add_value
+ * Add value to the bloom filter.
+ */
+static BloomFilter *
+bloom_add_value(BloomFilter *filter, uint32 value, bool *updated)
+{
+ int i;
+ uint32 big_h, h, d;
+
+ /* assume 'not updated' by default */
+ Assert(filter);
+
+ /* if we're in the sorted phase, we store the hashes directly */
+ if (BLOOM_IS_SORTED(filter))
+ {
+ /* how many uint32 hashes can we fit into the bitmap */
+ int maxvalues = filter->nbits / (8 * sizeof(uint32));
+
+ /* do not overflow the bitmap space or number of unsorted items */
+ Assert(filter->nvalues <= maxvalues);
+ Assert(filter->nvalues - filter->nsorted <= BLOOM_MAX_UNSORTED);
+
+ /*
+ * In this branch we always update the filter - we either add the
+ * hash to the unsorted part, or switch the filter to hashing.
+ */
+ if (updated)
+ *updated = true;
+
+ /*
+ * If the array is full, or if we reached the limit on unsorted
+ * items, try to compact the filter first, before attempting to
+ * add the new value.
+ */
+ if ((filter->nvalues == maxvalues) ||
+ (filter->nvalues - filter->nsorted == BLOOM_MAX_UNSORTED))
+ bloom_compact(filter);
+
+ /*
+ * Can we squeeze one more uint32 hash into the bitmap? Also make
+ * sure there's enough space in the bytea value first.
+ */
+ if (filter->nvalues < maxvalues)
+ {
+ Size len = VARSIZE_ANY(filter);
+ Size need = offsetof(BloomFilter, data) +
+ (filter->nvalues + 1) * sizeof(uint32);
+
+ /*
+ * We don't double the size here, as in the first place we care about
+ * reducing storage requirements, and the doubling happens automatically
+ * in memory contexts (so the repalloc should be cheap in most cases).
+ */
+ if (len < need)
+ {
+ filter = (BloomFilter *) repalloc(filter, need);
+ SET_VARSIZE(filter, need);
+ }
+
+ /* copy the new value into the filter */
+ memcpy(&filter->data[filter->nvalues * sizeof(uint32)],
+ &value, sizeof(uint32));
+
+ filter->nvalues++;
+
+ /* we're done */
+ return filter;
+ }
+
+ /* can't add any more exact hashes, so switch to hashing */
+ filter = bloom_switch_to_hashing(filter);
+ }
+
+ /* we better be in the hashing phase */
+ Assert(BLOOM_IS_HASHED(filter));
+
+ /* compute the hashes, used for the bloom filter */
+ big_h = ((uint32) DatumGetInt64(hash_uint32(value)));
+
+ h = big_h % filter->nbits;
+ d = big_h % (filter->nbits - 1);
+
+ /* compute the requested number of hashes */
+ for (i = 0; i < filter->nhashes; i++)
+ {
+ int byte = (h / 8);
+ int bit = (h % 8);
+
+ /* if the bit is not set, set it and remember we did that */
+ if (! (filter->data[byte] & (0x01 << bit)))
+ {
+ filter->data[byte] |= (0x01 << bit);
+ filter->nbits_set++;
+ if (updated)
+ *updated = true;
+ }
+
+ /* next bit */
+ h += d++;
+ if (h >= filter->nbits)
+ h -= filter->nbits;
+
+ if (d == filter->nbits)
+ d = 0;
+ }
+
+ return filter;
+}
+
+/*
+ * bloom_switch_to_hashing
+ * Switch the bloom filter from sorted to hashing mode.
+ */
+static BloomFilter *
+bloom_switch_to_hashing(BloomFilter *filter)
+{
+ int i;
+ uint32 *values;
+ Size len;
+ BloomFilter *newfilter;
+
+ Assert(filter->nbits % 8 == 0);
+
+ /*
+ * The new filter is allocated with all the memory, directly into
+ * the HASH phase.
+ */
+ len = offsetof(BloomFilter, data) + (filter->nbits / 8);
+
+ newfilter = (BloomFilter *) palloc0(len);
+
+ newfilter->nhashes = filter->nhashes;
+ newfilter->nbits = filter->nbits;
+ newfilter->flags |= BLOOM_FLAG_PHASE_HASH;
+
+ SET_VARSIZE(newfilter, len);
+
+ values = (uint32 *) filter->data;
+
+ for (i = 0; i < filter->nvalues; i++)
+ /* ignore the return value here, re don't repalloc in hashing mode */
+ bloom_add_value(newfilter, values[i], NULL);
+
+ /* free the original filter, return the newly allocated one */
+ pfree(filter);
+
+ return newfilter;
+}
+
+/*
+ * bloom_contains_value
+ * Check if the bloom filter contains a particular value.
+ */
+static bool
+bloom_contains_value(BloomFilter *filter, uint32 value)
+{
+ int i;
+ uint32 big_h, h, d;
+
+ Assert(filter);
+
+ /* in sorted mode we simply search the two arrays (sorted, unsorted) */
+ if (BLOOM_IS_SORTED(filter))
+ {
+ int i;
+ uint32 *values = (uint32 *) filter->data;
+
+ /* first search through the sorted part */
+ if ((filter->nsorted > 0) &&
+ (bsearch(&value, values, filter->nsorted, sizeof(uint32), cmp_uint32) != NULL))
+ return true;
+
+ /* now search through the unsorted part - linear search */
+ for (i = filter->nsorted; i < filter->nvalues; i++)
+ {
+ if (value == values[i])
+ return true;
+ }
+
+ /* nothing found */
+ return false;
+ }
+
+ /* now the regular hashing mode */
+ Assert(BLOOM_IS_HASHED(filter));
+
+ big_h = ((uint32) DatumGetInt64(hash_uint32(value)));
+
+ h = big_h % filter->nbits;
+ d = big_h % (filter->nbits - 1);
+
+ /* compute the requested number of hashes */
+ for (i = 0; i < filter->nhashes; i++)
+ {
+ int byte = (h / 8);
+ int bit = (h % 8);
+
+ /* if the bit is not set, the value is not there */
+ if (! (filter->data[byte] & (0x01 << bit)))
+ return false;
+
+ /* next bit */
+ h += d++;
+ if (h >= filter->nbits)
+ h -= filter->nbits;
+
+ if (d == filter->nbits)
+ d = 0;
+ }
+
+ /* all hashes found in bloom filter */
+ return true;
+}
+
+typedef struct BloomOpaque
+{
+ /*
+ * XXX At this point we only need a single proc (to compute the hash),
+ * but let's keep the array just like inclusion and minman opclasses,
+ * for consistency. We may need additional procs in the future.
+ */
+ FmgrInfo extra_procinfos[BLOOM_MAX_PROCNUMS];
+ bool extra_proc_missing[BLOOM_MAX_PROCNUMS];
+} BloomOpaque;
+
+static FmgrInfo *bloom_get_procinfo(BrinDesc *bdesc, uint16 attno,
+ uint16 procnum);
+
+
+Datum
+brin_bloom_opcinfo(PG_FUNCTION_ARGS)
+{
+ BrinOpcInfo *result;
+
+ /*
+ * opaque->strategy_procinfos is initialized lazily; here it is set to
+ * all-uninitialized by palloc0 which sets fn_oid to InvalidOid.
+ *
+ * bloom indexes only store the filter as a single BYTEA column
+ */
+
+ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) +
+ sizeof(BloomOpaque));
+ result->oi_nstored = 1;
+ result->oi_regular_nulls = true;
+ result->oi_opaque = (BloomOpaque *)
+ MAXALIGN((char *) result + SizeofBrinOpcInfo(1));
+ result->oi_typcache[0] = lookup_type_cache(BRINBLOOMSUMMARYOID, 0);
+
+ PG_RETURN_POINTER(result);
+}
+
+/*
+ * brin_bloom_get_ndistinct
+ * Determine the ndistinct value used to size bloom filter.
+ *
+ * Tweak the ndistinct value based on the pagesPerRange value. First,
+ * if it's negative, it's assumed to be relative to maximum number of
+ * tuples in the range (assuming each page gets MaxHeapTuplesPerPage
+ * tuples, which is likely a significant over-estimate). We also clamp
+ * the value, not to over-size the bloom filter unnecessarily.
+ *
+ * XXX We can only do this when the pagesPerRange value was supplied.
+ * If it wasn't, it has to be a read-only access to the index, in which
+ * case we don't really care. But perhaps we should fall-back to the
+ * default pagesPerRange value?
+ *
+ * XXX We might also fetch info about ndistinct estimate for the column,
+ * and compute the expected number of distinct values in a range. But
+ * that may be tricky due to data being sorted in various ways, so it
+ * seems better to rely on the upper estimate.
+ */
+static int
+brin_bloom_get_ndistinct(BrinDesc *bdesc, BloomOptions *opts)
+{
+ double ndistinct;
+ double maxtuples;
+ BlockNumber pagesPerRange;
+
+ pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
+ ndistinct = BloomGetNDistinctPerRange(opts);
+
+ Assert(BlockNumberIsValid(pagesPerRange));
+
+ maxtuples = MaxHeapTuplesPerPage * pagesPerRange;
+
+ /*
+ * Similarly to n_distinct, negative values are relative - in this
+ * case to maximum number of tuples in the page range (maxtuples).
+ */
+ if (ndistinct < 0)
+ ndistinct = (-ndistinct) * maxtuples;
+
+ /*
+ * Positive values are to be used directly, but we still apply a
+ * couple of safeties no to use unreasonably small bloom filters.
+ */
+ ndistinct = Max(ndistinct, BLOOM_MIN_NDISTINCT_PER_RANGE);
+
+ /*
+ * And don't use more than the maximum possible number of tuples,
+ * in the range, which would be entirely wasteful.
+ */
+ ndistinct = Min(ndistinct, maxtuples);
+
+ return (int) ndistinct;
+}
+
+static double
+brin_bloom_get_fp_rate(BrinDesc *bdesc, BloomOptions *opts)
+{
+ return BloomGetFalsePositiveRate(opts);
+}
+
+/*
+ * Examine the given index tuple (which contains partial status of a certain
+ * page range) by comparing it to the given value that comes from another heap
+ * tuple. If the new value is outside the bloom filter specified by the
+ * existing tuple values, update the index tuple and return true. Otherwise,
+ * return false and do not modify in this case.
+ */
+Datum
+brin_bloom_add_value(PG_FUNCTION_ARGS)
+{
+ BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+ BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
+ Datum newval = PG_GETARG_DATUM(2);
+ bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3);
+ BloomOptions *opts = (BloomOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION();
+ FmgrInfo *hashFn;
+ uint32 hashValue;
+ bool updated = false;
+ AttrNumber attno;
+ BloomFilter *filter;
+
+ Assert(!isnull);
+
+ attno = column->bv_attno;
+
+ /*
+ * If this is the first non-null value, we need to initialize the bloom
+ * filter. Otherwise just extract the existing bloom filter from BrinValues.
+ */
+ if (column->bv_allnulls)
+ {
+ filter = bloom_init(brin_bloom_get_ndistinct(bdesc, opts),
+ brin_bloom_get_fp_rate(bdesc, opts));
+ column->bv_values[0] = PointerGetDatum(filter);
+ column->bv_allnulls = false;
+ updated = true;
+ }
+ else
+ filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]);
+
+ /*
+ * Compute the hash of the new value, using the supplied hash function,
+ * and then add the hash value to the bloom filter.
+ */
+ hashFn = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH);
+
+ hashValue = DatumGetUInt32(FunctionCall1Coll(hashFn, colloid, newval));
+
+ filter = bloom_add_value(filter, hashValue, &updated);
+
+ column->bv_values[0] = PointerGetDatum(filter);
+
+ PG_RETURN_BOOL(updated);
+}
+
+/*
+ * 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 bloom
+ * filter. Return true if so, false otherwise.
+ */
+Datum
+brin_bloom_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();
+ AttrNumber attno;
+ Datum value;
+ Datum matches;
+ FmgrInfo *finfo;
+ uint32 hashValue;
+ BloomFilter *filter;
+ int keyno;
+
+ filter = (BloomFilter *) PG_DETOAST_DATUM(column->bv_values[0]);
+
+ Assert(filter);
+
+ matches = true;
+
+ 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));
+
+ attno = key->sk_attno;
+ value = key->sk_argument;
+
+ switch (key->sk_strategy)
+ {
+ case BloomEqualStrategyNumber:
+
+ /*
+ * In the equality case (WHERE col = someval), we want to return
+ * the current page range if the minimum value in the range <=
+ * scan key, and the maximum value >= scan key.
+ */
+ finfo = bloom_get_procinfo(bdesc, attno, PROCNUM_HASH);
+
+ hashValue = DatumGetUInt32(FunctionCall1Coll(finfo, colloid, value));
+ matches &= bloom_contains_value(filter, hashValue);
+
+ break;
+ default:
+ /* shouldn't happen */
+ elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+ matches = 0;
+ break;
+ }
+
+ if (!matches)
+ break;
+ }
+
+ PG_RETURN_DATUM(matches);
+}
+
+/*
+ * Given two BrinValues, update the first of them as a union of the summary
+ * values contained in both. The second one is untouched.
+ *
+ * XXX We assume the bloom filters have the same parameters fow now. In the
+ * future we should have 'can union' function, to decide if we can combine
+ * two particular bloom filters.
+ */
+Datum
+brin_bloom_union(PG_FUNCTION_ARGS)
+{
+ BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
+ BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
+ BloomFilter *filter_a;
+ BloomFilter *filter_b;
+
+ Assert(col_a->bv_attno == col_b->bv_attno);
+ Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
+
+ filter_a = (BloomFilter *) PG_DETOAST_DATUM(col_a->bv_values[0]);
+ filter_b = (BloomFilter *) PG_DETOAST_DATUM(col_b->bv_values[0]);
+
+ /* make sure neither of the bloom filters is NULL */
+ Assert(filter_a && filter_b);
+ Assert(filter_a->nbits == filter_b->nbits);
+
+ /*
+ * Merging of the filters depends on the phase of both filters.
+ */
+ if (BLOOM_IS_SORTED(filter_b))
+ {
+ /*
+ * Simply read all items from 'b' and add them to 'a' (the phase of
+ * 'a' does not really matter).
+ */
+ int i;
+ uint32 *values = (uint32 *) filter_b->data;
+
+ for (i = 0; i < filter_b->nvalues; i++)
+ filter_a = bloom_add_value(filter_a, values[i], NULL);
+
+ col_a->bv_values[0] = PointerGetDatum(filter_a);
+ }
+ else if (BLOOM_IS_SORTED(filter_a))
+ {
+ /*
+ * 'b' hashed, 'a' sorted - copy 'b' into 'a' and then add all values
+ * from 'a' into the new copy.
+ */
+ int i;
+ BloomFilter *filter_c;
+ uint32 *values = (uint32 *) filter_a->data;
+
+ filter_c = (BloomFilter *) PG_DETOAST_DATUM(datumCopy(PointerGetDatum(filter_b), false, -1));
+
+ for (i = 0; i < filter_a->nvalues; i++)
+ filter_c = bloom_add_value(filter_c, values[i], NULL);
+
+ col_a->bv_values[0] = PointerGetDatum(filter_c);
+ }
+ else if (BLOOM_IS_HASHED(filter_a))
+ {
+ /*
+ * 'b' hashed, 'a' hashed - merge the bitmaps by OR
+ */
+ int i;
+ int nbytes = (filter_a->nbits + 7) / 8;
+
+ /* we better have "compatible" bloom filters */
+ Assert(filter_a->nbits == filter_b->nbits);
+ Assert(filter_a->nhashes == filter_b->nhashes);
+
+ for (i = 0; i < nbytes; i++)
+ filter_a->data[i] |= filter_b->data[i];
+ }
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Cache and return inclusion opclass support procedure
+ *
+ * Return the procedure corresponding to the given function support number
+ * or null if it is not exists.
+ */
+static FmgrInfo *
+bloom_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
+{
+ BloomOpaque *opaque;
+ uint16 basenum = procnum - PROCNUM_BASE;
+
+ /*
+ * We cache these in the opaque struct, to avoid repetitive syscache
+ * lookups.
+ */
+ opaque = (BloomOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+ /*
+ * If we already searched for this proc and didn't find it, don't bother
+ * searching again.
+ */
+ if (opaque->extra_proc_missing[basenum])
+ return NULL;
+
+ if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
+ {
+ if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
+ procnum)))
+ {
+ fmgr_info_copy(&opaque->extra_procinfos[basenum],
+ index_getprocinfo(bdesc->bd_index, attno, procnum),
+ bdesc->bd_context);
+ }
+ else
+ {
+ opaque->extra_proc_missing[basenum] = true;
+ return NULL;
+ }
+ }
+
+ return &opaque->extra_procinfos[basenum];
+}
+
+Datum
+brin_bloom_options(PG_FUNCTION_ARGS)
+{
+ local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+ init_local_reloptions(relopts, sizeof(BloomOptions));
+
+ add_local_real_reloption(relopts, "n_distinct_per_range",
+ "number of distinct items expected in a BRIN page range",
+ BLOOM_DEFAULT_NDISTINCT_PER_RANGE,
+ -1.0, INT_MAX, offsetof(BloomOptions, nDistinctPerRange));
+
+ add_local_real_reloption(relopts, "false_positive_rate",
+ "desired false-positive rate for the bloom filters",
+ BLOOM_DEFAULT_FALSE_POSITIVE_RATE,
+ 0.001, 1.0, offsetof(BloomOptions, falsePositiveRate));
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * brin_bloom_summary_in
+ * - input routine for type brin_bloom_summary.
+ *
+ * brin_bloom_summary is only used internally to represent summaries
+ * in BRIN bloom indexes, so it has no operations of its own, and we
+ * disallow input too.
+ */
+Datum
+brin_bloom_summary_in(PG_FUNCTION_ARGS)
+{
+ /*
+ * brin_bloom_summary stores the data in binary form and parsing
+ * text input is not needed, so disallow this.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+
+/*
+ * brin_bloom_summary_out
+ * - output routine for type brin_bloom_summary.
+ *
+ * BRIN bloom summaries are serialized into a bytea value, but we want
+ * to output something nicer humans can understand.
+ */
+Datum
+brin_bloom_summary_out(PG_FUNCTION_ARGS)
+{
+ BloomFilter *filter;
+ StringInfoData str;
+
+ initStringInfo(&str);
+ appendStringInfoChar(&str, '{');
+
+ /*
+ * XXX not sure the detoasting is necessary (probably not, this
+ * can only be in an index).
+ */
+ filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+
+ if (BLOOM_IS_HASHED(filter))
+ {
+ appendStringInfo(&str, "mode: hashed nhashes: %u nbits: %u nbits_set: %u",
+ filter->nhashes, filter->nbits, filter->nbits_set);
+ }
+ else
+ {
+ appendStringInfo(&str, "mode: sorted nvalues: %u nsorted: %u",
+ filter->nvalues, filter->nsorted);
+ /* TODO include the sorted/unsorted values */
+ }
+
+ appendStringInfoChar(&str, '}');
+
+ PG_RETURN_CSTRING(str.data);
+}
+
+/*
+ * brin_bloom_summary_recv
+ * - binary input routine for type brin_bloom_summary.
+ */
+Datum
+brin_bloom_summary_recv(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * brin_bloom_summary_send
+ * - binary output routine for type brin_bloom_summary.
+ *
+ * BRIN bloom summaries are serialized in a bytea value (although the
+ * type is named differently), so let's just send that.
+ */
+Datum
+brin_bloom_summary_send(PG_FUNCTION_ARGS)
+{
+ return byteasend(fcinfo);
+}
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 0987878dd2..b02298c0aa 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -22,6 +22,8 @@ typedef struct BrinOptions
int32 vl_len_; /* varlena header (do not touch directly!) */
BlockNumber pagesPerRange;
bool autosummarize;
+ double nDistinctPerRange; /* number of distinct values per range */
+ double falsePositiveRate; /* false positive for bloom filter */
} BrinOptions;
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 7c4f3da0a0..e3c9d47503 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -58,6 +58,10 @@ typedef struct BrinDesc
/* total number of Datum entries that are stored on-disk for all columns */
int bd_totalstored;
+ /* parameters for sizing bloom filter (BRIN bloom opclasses) */
+ double bd_nDistinctPerRange;
+ double bd_falsePositiveRange;
+
/* per-column info; bd_tupdesc->natts entries long */
BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER];
} BrinDesc;
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 1dfb6fd373..12033d48ec 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1705,6 +1705,11 @@
amoprighttype => 'bytea', amopstrategy => '5', amopopr => '>(bytea,bytea)',
amopmethod => 'brin' },
+# bloom bytea
+{ amopfamily => 'brin/bytea_bloom_ops', amoplefttype => 'bytea',
+ amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)',
+ amopmethod => 'brin' },
+
# minmax "char"
{ amopfamily => 'brin/char_minmax_ops', amoplefttype => 'char',
amoprighttype => 'char', amopstrategy => '1', amopopr => '<(char,char)',
@@ -1722,6 +1727,11 @@
amoprighttype => 'char', amopstrategy => '5', amopopr => '>(char,char)',
amopmethod => 'brin' },
+# bloom "char"
+{ amopfamily => 'brin/char_bloom_ops', amoplefttype => 'char',
+ amoprighttype => 'char', amopstrategy => '1', amopopr => '=(char,char)',
+ amopmethod => 'brin' },
+
# minmax name
{ amopfamily => 'brin/name_minmax_ops', amoplefttype => 'name',
amoprighttype => 'name', amopstrategy => '1', amopopr => '<(name,name)',
@@ -1739,6 +1749,11 @@
amoprighttype => 'name', amopstrategy => '5', amopopr => '>(name,name)',
amopmethod => 'brin' },
+# bloom name
+{ amopfamily => 'brin/name_bloom_ops', amoplefttype => 'name',
+ amoprighttype => 'name', amopstrategy => '1', amopopr => '=(name,name)',
+ amopmethod => 'brin' },
+
# minmax integer
{ amopfamily => 'brin/integer_minmax_ops', amoplefttype => 'int8',
@@ -1885,6 +1900,44 @@
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
amopmethod => 'brin' },
+# bloom integer
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int8,int8)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int8,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int8,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int2,int8)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int2,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int4,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '=(int4,int8)',
+ amopmethod => 'brin' },
+
# minmax text
{ amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text',
amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)',
@@ -1902,6 +1955,11 @@
amoprighttype => 'text', amopstrategy => '5', amopopr => '>(text,text)',
amopmethod => 'brin' },
+# bloom text
+{ amopfamily => 'brin/text_bloom_ops', amoplefttype => 'text',
+ amoprighttype => 'text', amopstrategy => '1', amopopr => '=(text,text)',
+ amopmethod => 'brin' },
+
# minmax oid
{ amopfamily => 'brin/oid_minmax_ops', amoplefttype => 'oid',
amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)',
@@ -1919,6 +1977,11 @@
amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)',
amopmethod => 'brin' },
+# bloom oid
+{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)',
+ amopmethod => 'brin' },
+
# minmax tid
{ amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid',
amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)',
@@ -1936,6 +1999,11 @@
amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)',
amopmethod => 'brin' },
+# tid oid
+{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)',
+ amopmethod => 'brin' },
+
# minmax float (float4, float8)
{ amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4',
@@ -2002,6 +2070,20 @@
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
amopmethod => 'brin' },
+# bloom float
+{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float4,float8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float8,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)',
+ amopmethod => 'brin' },
+
# minmax macaddr
{ amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr',
amoprighttype => 'macaddr', amopstrategy => '1',
@@ -2019,6 +2101,11 @@
amoprighttype => 'macaddr', amopstrategy => '5',
amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' },
+# bloom macaddr
+{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '1',
+ amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' },
+
# minmax macaddr8
{ amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8',
amoprighttype => 'macaddr8', amopstrategy => '1',
@@ -2036,6 +2123,11 @@
amoprighttype => 'macaddr8', amopstrategy => '5',
amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' },
+# bloom macaddr8
+{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '1',
+ amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' },
+
# minmax inet
{ amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet',
amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)',
@@ -2053,6 +2145,11 @@
amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)',
amopmethod => 'brin' },
+# bloom inet
+{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)',
+ amopmethod => 'brin' },
+
# inclusion inet
{ amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet',
amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)',
@@ -2090,6 +2187,11 @@
amoprighttype => 'bpchar', amopstrategy => '5', amopopr => '>(bpchar,bpchar)',
amopmethod => 'brin' },
+# bloom character
+{ amopfamily => 'brin/bpchar_bloom_ops', amoplefttype => 'bpchar',
+ amoprighttype => 'bpchar', amopstrategy => '1', amopopr => '=(bpchar,bpchar)',
+ amopmethod => 'brin' },
+
# minmax time without time zone
{ amopfamily => 'brin/time_minmax_ops', amoplefttype => 'time',
amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)',
@@ -2107,6 +2209,11 @@
amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
amopmethod => 'brin' },
+# bloom time without time zone
+{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)',
+ amopmethod => 'brin' },
+
# minmax datetime (date, timestamp, timestamptz)
{ amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp',
@@ -2253,6 +2360,44 @@
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' },
+# bloom datetime (date, timestamp, timestamptz)
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '1', amopopr => '=(timestamp,date)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '=(date,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '=(date,timestamptz)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '1',
+ amopopr => '=(timestamptz,date)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' },
+
# minmax interval
{ amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval',
amoprighttype => 'interval', amopstrategy => '1',
@@ -2270,6 +2415,11 @@
amoprighttype => 'interval', amopstrategy => '5',
amopopr => '>(interval,interval)', amopmethod => 'brin' },
+# bloom interval
+{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '1',
+ amopopr => '=(interval,interval)', amopmethod => 'brin' },
+
# minmax time with time zone
{ amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz',
amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)',
@@ -2287,6 +2437,11 @@
amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)',
amopmethod => 'brin' },
+# bloom time with time zone
+{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)',
+ amopmethod => 'brin' },
+
# minmax bit
{ amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit',
amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)',
@@ -2338,6 +2493,11 @@
amoprighttype => 'numeric', amopstrategy => '5',
amopopr => '>(numeric,numeric)', amopmethod => 'brin' },
+# bloom numeric
+{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '1',
+ amopopr => '=(numeric,numeric)', amopmethod => 'brin' },
+
# minmax uuid
{ amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid',
amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)',
@@ -2355,6 +2515,11 @@
amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)',
amopmethod => 'brin' },
+# bloom uuid
+{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)',
+ amopmethod => 'brin' },
+
# inclusion range types
{ amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange',
amoprighttype => 'anyrange', amopstrategy => '1',
@@ -2416,6 +2581,11 @@
amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)',
amopmethod => 'brin' },
+# bloom pg_lsn
+{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)',
+ amopmethod => 'brin' },
+
# inclusion box
{ amopfamily => 'brin/box_inclusion_ops', amoplefttype => 'box',
amoprighttype => 'box', amopstrategy => '1', amopopr => '<<(box,box)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 37b580883f..4e35bb3459 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -770,6 +770,24 @@
{ amprocfamily => 'brin/bytea_minmax_ops', amproclefttype => 'bytea',
amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom bytea
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/bytea_bloom_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '11', amproc => 'hashvarlena' },
+
# minmax "char"
{ amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char',
amprocrighttype => 'char', amprocnum => '1',
@@ -783,6 +801,24 @@
{ amprocfamily => 'brin/char_minmax_ops', amproclefttype => 'char',
amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom "char"
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/char_bloom_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '11', amproc => 'hashchar' },
+
# minmax name
{ amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name',
amprocrighttype => 'name', amprocnum => '1',
@@ -796,6 +832,24 @@
{ amprocfamily => 'brin/name_minmax_ops', amproclefttype => 'name',
amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom name
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/name_bloom_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '11', amproc => 'hashname' },
+
# minmax integer: int2, int4, int8
{ amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int8',
amprocrighttype => 'int8', amprocnum => '1',
@@ -897,6 +951,58 @@
{ amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4',
amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom integer: int2, int4, int8
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '11', amproc => 'hashint8' },
+
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '11', amproc => 'hashint2' },
+
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '11', amproc => 'hashint4' },
+
# minmax text
{ amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text',
amprocrighttype => 'text', amprocnum => '1',
@@ -910,6 +1016,24 @@
{ amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text',
amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom text
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/text_bloom_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '11', amproc => 'hashtext' },
+
# minmax oid
{ amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid',
amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' },
@@ -922,6 +1046,23 @@
{ amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid',
amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom oid
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '11', amproc => 'hashoid' },
+
# minmax tid
{ amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid',
amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' },
@@ -934,6 +1075,23 @@
{ amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid',
amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom tid
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' },
+
# minmax float
{ amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4',
amprocrighttype => 'float4', amprocnum => '1',
@@ -984,6 +1142,45 @@
amprocrighttype => 'float4', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom float
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '11',
+ amproc => 'hashfloat4' },
+
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '11',
+ amproc => 'hashfloat8' },
+
# minmax macaddr
{ amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr',
amprocrighttype => 'macaddr', amprocnum => '1',
@@ -998,6 +1195,26 @@
amprocrighttype => 'macaddr', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom macaddr
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '11',
+ amproc => 'hashmacaddr' },
+
# minmax macaddr8
{ amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8',
amprocrighttype => 'macaddr8', amprocnum => '1',
@@ -1012,6 +1229,26 @@
amprocrighttype => 'macaddr8', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom macaddr8
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '11',
+ amproc => 'hashmacaddr8' },
+
# minmax inet
{ amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '1',
@@ -1025,6 +1262,24 @@
{ amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom inet
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '11', amproc => 'hashinet' },
+
# inclusion inet
{ amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '1',
@@ -1059,6 +1314,26 @@
amprocrighttype => 'bpchar', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom character
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/bpchar_bloom_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '11',
+ amproc => 'hashchar' },
+
# minmax time without time zone
{ amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time',
amprocrighttype => 'time', amprocnum => '1',
@@ -1072,6 +1347,24 @@
{ amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time',
amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom time without time zone
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '11', amproc => 'time_hash' },
+
# minmax datetime (date, timestamp, timestamptz)
{ amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp',
amprocrighttype => 'timestamp', amprocnum => '1',
@@ -1179,6 +1472,62 @@
amprocrighttype => 'timestamptz', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom datetime (date, timestamp, timestamptz)
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '11',
+ amproc => 'timestamp_hash' },
+
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '11',
+ amproc => 'timestamp_hash' },
+
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '11', amproc => 'hashint4' },
+
# minmax interval
{ amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval',
amprocrighttype => 'interval', amprocnum => '1',
@@ -1193,6 +1542,26 @@
amprocrighttype => 'interval', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom interval
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '11',
+ amproc => 'interval_hash' },
+
# minmax time with time zone
{ amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz',
amprocrighttype => 'timetz', amprocnum => '1',
@@ -1207,6 +1576,26 @@
amprocrighttype => 'timetz', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom time with time zone
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '11',
+ amproc => 'timetz_hash' },
+
# minmax bit
{ amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit',
amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' },
@@ -1247,6 +1636,26 @@
amprocrighttype => 'numeric', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom numeric
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '11',
+ amproc => 'hash_numeric' },
+
# minmax uuid
{ amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid',
amprocrighttype => 'uuid', amprocnum => '1',
@@ -1260,6 +1669,24 @@
{ amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid',
amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' },
+# bloom uuid
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '11', amproc => 'uuid_hash' },
+
# inclusion range types
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1',
@@ -1295,6 +1722,26 @@
amprocrighttype => 'pg_lsn', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# bloom pg_lsn
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '1',
+ amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '2',
+ amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '3',
+ amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '4',
+ amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '5',
+ amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '11',
+ amproc => 'pg_lsn_hash' },
+
# inclusion box
{ amprocfamily => 'brin/box_inclusion_ops', amproclefttype => 'box',
amprocrighttype => 'box', amprocnum => '1',
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index f2342bb328..ca747a03b9 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -257,67 +257,130 @@
{ opcmethod => 'brin', opcname => 'bytea_minmax_ops',
opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea',
opckeytype => 'bytea' },
+{ opcmethod => 'brin', opcname => 'bytea_bloom_ops',
+ opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea',
+ opckeytype => 'bytea', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'char_minmax_ops',
opcfamily => 'brin/char_minmax_ops', opcintype => 'char',
opckeytype => 'char' },
+{ opcmethod => 'brin', opcname => 'char_bloom_ops',
+ opcfamily => 'brin/char_bloom_ops', opcintype => 'char',
+ opckeytype => 'char', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'name_minmax_ops',
opcfamily => 'brin/name_minmax_ops', opcintype => 'name',
opckeytype => 'name' },
+{ opcmethod => 'brin', opcname => 'name_bloom_ops',
+ opcfamily => 'brin/name_bloom_ops', opcintype => 'name',
+ opckeytype => 'name', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int8_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8',
opckeytype => 'int8' },
+{ opcmethod => 'brin', opcname => 'int8_bloom_ops',
+ opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8',
+ opckeytype => 'int8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int2_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2',
opckeytype => 'int2' },
+{ opcmethod => 'brin', opcname => 'int2_bloom_ops',
+ opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2',
+ opckeytype => 'int2', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int4_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4',
opckeytype => 'int4' },
+{ opcmethod => 'brin', opcname => 'int4_bloom_ops',
+ opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4',
+ opckeytype => 'int4', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'text_minmax_ops',
opcfamily => 'brin/text_minmax_ops', opcintype => 'text',
opckeytype => 'text' },
+{ opcmethod => 'brin', opcname => 'text_bloom_ops',
+ opcfamily => 'brin/text_bloom_ops', opcintype => 'text',
+ opckeytype => 'text', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'oid_minmax_ops',
opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' },
+{ opcmethod => 'brin', opcname => 'oid_bloom_ops',
+ opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid',
+ opckeytype => 'oid', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'tid_minmax_ops',
opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' },
+{ opcmethod => 'brin', opcname => 'tid_bloom_ops',
+ opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid',
+ opcdefault => 'f'},
{ opcmethod => 'brin', opcname => 'float4_minmax_ops',
opcfamily => 'brin/float_minmax_ops', opcintype => 'float4',
opckeytype => 'float4' },
+{ opcmethod => 'brin', opcname => 'float4_bloom_ops',
+ opcfamily => 'brin/float_bloom_ops', opcintype => 'float4',
+ opckeytype => 'float4', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'float8_minmax_ops',
opcfamily => 'brin/float_minmax_ops', opcintype => 'float8',
opckeytype => 'float8' },
+{ opcmethod => 'brin', opcname => 'float8_bloom_ops',
+ opcfamily => 'brin/float_bloom_ops', opcintype => 'float8',
+ opckeytype => 'float8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr_minmax_ops',
opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr',
opckeytype => 'macaddr' },
+{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops',
+ opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr',
+ opckeytype => 'macaddr', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr8_minmax_ops',
opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8',
opckeytype => 'macaddr8' },
+{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops',
+ opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8',
+ opckeytype => 'macaddr8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'inet_minmax_ops',
opcfamily => 'brin/network_minmax_ops', opcintype => 'inet',
opcdefault => 'f', opckeytype => 'inet' },
+{ opcmethod => 'brin', opcname => 'inet_bloom_ops',
+ opcfamily => 'brin/network_bloom_ops', opcintype => 'inet',
+ opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'inet_inclusion_ops',
opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet',
opckeytype => 'inet' },
{ opcmethod => 'brin', opcname => 'bpchar_minmax_ops',
opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar',
opckeytype => 'bpchar' },
+{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops',
+ opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar',
+ opckeytype => 'bpchar', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'time_minmax_ops',
opcfamily => 'brin/time_minmax_ops', opcintype => 'time',
opckeytype => 'time' },
+{ opcmethod => 'brin', opcname => 'time_bloom_ops',
+ opcfamily => 'brin/time_bloom_ops', opcintype => 'time',
+ opckeytype => 'time', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'date_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date',
opckeytype => 'date' },
+{ opcmethod => 'brin', opcname => 'date_bloom_ops',
+ opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date',
+ opckeytype => 'date', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamp_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp',
opckeytype => 'timestamp' },
+{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops',
+ opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp',
+ opckeytype => 'timestamp', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamptz_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz',
opckeytype => 'timestamptz' },
+{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops',
+ opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz',
+ opckeytype => 'timestamptz', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'interval_minmax_ops',
opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval',
opckeytype => 'interval' },
+{ opcmethod => 'brin', opcname => 'interval_bloom_ops',
+ opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval',
+ opckeytype => 'interval', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timetz_minmax_ops',
opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz',
opckeytype => 'timetz' },
+{ opcmethod => 'brin', opcname => 'timetz_bloom_ops',
+ opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz',
+ opckeytype => 'timetz', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'bit_minmax_ops',
opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' },
{ opcmethod => 'brin', opcname => 'varbit_minmax_ops',
@@ -326,18 +389,27 @@
{ opcmethod => 'brin', opcname => 'numeric_minmax_ops',
opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric',
opckeytype => 'numeric' },
+{ opcmethod => 'brin', opcname => 'numeric_bloom_ops',
+ opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric',
+ opckeytype => 'numeric', opcdefault => 'f' },
# no brin opclass for record, anyarray
{ opcmethod => 'brin', opcname => 'uuid_minmax_ops',
opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid',
opckeytype => 'uuid' },
+{ opcmethod => 'brin', opcname => 'uuid_bloom_ops',
+ opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid',
+ opckeytype => 'uuid', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'range_inclusion_ops',
opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange',
opckeytype => 'anyrange' },
{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops',
opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn',
opckeytype => 'pg_lsn' },
+{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops',
+ opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn',
+ opckeytype => 'pg_lsn', opcdefault => 'f' },
# no brin opclass for enum, tsvector, tsquery, jsonb
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index cf0fb325b3..8875079698 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -180,50 +180,88 @@
opfmethod => 'gin', opfname => 'jsonb_path_ops' },
{ oid => '4054',
opfmethod => 'brin', opfname => 'integer_minmax_ops' },
+{ oid => '8100',
+ opfmethod => 'brin', opfname => 'integer_bloom_ops' },
{ oid => '4055',
opfmethod => 'brin', opfname => 'numeric_minmax_ops' },
{ oid => '4056',
opfmethod => 'brin', opfname => 'text_minmax_ops' },
+{ oid => '8101',
+ opfmethod => 'brin', opfname => 'text_bloom_ops' },
+{ oid => '8102',
+ opfmethod => 'brin', opfname => 'numeric_bloom_ops' },
{ oid => '4058',
opfmethod => 'brin', opfname => 'timetz_minmax_ops' },
+{ oid => '8103',
+ opfmethod => 'brin', opfname => 'timetz_bloom_ops' },
{ oid => '4059',
opfmethod => 'brin', opfname => 'datetime_minmax_ops' },
+{ oid => '8104',
+ opfmethod => 'brin', opfname => 'datetime_bloom_ops' },
{ oid => '4062',
opfmethod => 'brin', opfname => 'char_minmax_ops' },
+{ oid => '8105',
+ opfmethod => 'brin', opfname => 'char_bloom_ops' },
{ oid => '4064',
opfmethod => 'brin', opfname => 'bytea_minmax_ops' },
+{ oid => '8106',
+ opfmethod => 'brin', opfname => 'bytea_bloom_ops' },
{ oid => '4065',
opfmethod => 'brin', opfname => 'name_minmax_ops' },
+{ oid => '8107',
+ opfmethod => 'brin', opfname => 'name_bloom_ops' },
{ oid => '4068',
opfmethod => 'brin', opfname => 'oid_minmax_ops' },
+{ oid => '8108',
+ opfmethod => 'brin', opfname => 'oid_bloom_ops' },
{ oid => '4069',
opfmethod => 'brin', opfname => 'tid_minmax_ops' },
+{ oid => '8123',
+ opfmethod => 'brin', opfname => 'tid_bloom_ops' },
{ oid => '4070',
opfmethod => 'brin', opfname => 'float_minmax_ops' },
+{ oid => '8109',
+ opfmethod => 'brin', opfname => 'float_bloom_ops' },
{ oid => '4074',
opfmethod => 'brin', opfname => 'macaddr_minmax_ops' },
+{ oid => '8110',
+ opfmethod => 'brin', opfname => 'macaddr_bloom_ops' },
{ oid => '4109',
opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' },
+{ oid => '8111',
+ opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' },
{ oid => '4075',
opfmethod => 'brin', opfname => 'network_minmax_ops' },
{ oid => '4102',
opfmethod => 'brin', opfname => 'network_inclusion_ops' },
+{ oid => '8112',
+ opfmethod => 'brin', opfname => 'network_bloom_ops' },
{ oid => '4076',
opfmethod => 'brin', opfname => 'bpchar_minmax_ops' },
+{ oid => '8113',
+ opfmethod => 'brin', opfname => 'bpchar_bloom_ops' },
{ oid => '4077',
opfmethod => 'brin', opfname => 'time_minmax_ops' },
+{ oid => '8114',
+ opfmethod => 'brin', opfname => 'time_bloom_ops' },
{ oid => '4078',
opfmethod => 'brin', opfname => 'interval_minmax_ops' },
+{ oid => '8115',
+ opfmethod => 'brin', opfname => 'interval_bloom_ops' },
{ oid => '4079',
opfmethod => 'brin', opfname => 'bit_minmax_ops' },
{ oid => '4080',
opfmethod => 'brin', opfname => 'varbit_minmax_ops' },
{ oid => '4081',
opfmethod => 'brin', opfname => 'uuid_minmax_ops' },
+{ oid => '8116',
+ opfmethod => 'brin', opfname => 'uuid_bloom_ops' },
{ oid => '4103',
opfmethod => 'brin', opfname => 'range_inclusion_ops' },
{ oid => '4082',
opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' },
+{ oid => '8117',
+ opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' },
{ oid => '4104',
opfmethod => 'brin', opfname => 'box_inclusion_ops' },
{ oid => '5000',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 925b262b60..dc17d4ce38 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8127,6 +8127,26 @@
proargtypes => 'internal internal internal',
prosrc => 'brin_inclusion_union' },
+# BRIN bloom
+{ oid => '8118', descr => 'BRIN bloom support',
+ proname => 'brin_bloom_opcinfo', prorettype => 'internal',
+ proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' },
+{ oid => '8119', descr => 'BRIN bloom support',
+ proname => 'brin_bloom_add_value', prorettype => 'bool',
+ proargtypes => 'internal internal internal internal',
+ prosrc => 'brin_bloom_add_value' },
+{ oid => '8120', descr => 'BRIN bloom support',
+ proname => 'brin_bloom_consistent', prorettype => 'bool',
+ proargtypes => 'internal internal internal int4',
+ prosrc => 'brin_bloom_consistent' },
+{ oid => '8121', descr => 'BRIN bloom support',
+ proname => 'brin_bloom_union', prorettype => 'bool',
+ proargtypes => 'internal internal internal',
+ prosrc => 'brin_bloom_union' },
+{ oid => '8122', descr => 'BRIN bloom support',
+ proname => 'brin_bloom_options', prorettype => 'void', proisstrict => 'f',
+ proargtypes => 'internal', prosrc => 'brin_bloom_options' },
+
# userlock replacements
{ oid => '2880', descr => 'obtain exclusive advisory lock',
proname => 'pg_advisory_lock', provolatile => 'v', proparallel => 'r',
@@ -10973,3 +10993,17 @@
prosrc => 'unicode_is_normalized' },
]
+
+{ oid => '9035', descr => 'I/O',
+ proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary',
+ proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' },
+{ oid => '9036', descr => 'I/O',
+ proname => 'brin_bloom_summary_out', prorettype => 'cstring',
+ proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' },
+{ oid => '9037', descr => 'I/O',
+ proname => 'brin_bloom_summary_recv', provolatile => 's',
+ prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal',
+ prosrc => 'brin_bloom_summary_recv' },
+{ oid => '9038', descr => 'I/O',
+ proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index b2cec07416..a41c2e5418 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -631,3 +631,10 @@
typalign => 'd', typstorage => 'x' },
]
+
+{ oid => '9034', oid_symbol => 'BRINBLOOMSUMMARYOID',
+ descr => 'BRIN bloom summary',
+ typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S',
+ typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out',
+ typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send',
+ typalign => 'i', typstorage => 'x', typcollation => 'default' },
diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out
new file mode 100644
index 0000000000..d58a4a483c
--- /dev/null
+++ b/src/test/regress/expected/brin_bloom.out
@@ -0,0 +1,456 @@
+CREATE TABLE brintest_bloom (byteacol bytea,
+ charcol "char",
+ namecol name,
+ int8col bigint,
+ int2col smallint,
+ int4col integer,
+ textcol text,
+ oidcol oid,
+ float4col real,
+ float8col double precision,
+ macaddrcol macaddr,
+ inetcol inet,
+ cidrcol cidr,
+ bpcharcol character,
+ datecol date,
+ timecol time without time zone,
+ timestampcol timestamp without time zone,
+ timestamptzcol timestamp with time zone,
+ intervalcol interval,
+ timetzcol time with time zone,
+ numericcol numeric,
+ uuidcol uuid,
+ lsncol pg_lsn
+) WITH (fillfactor=10);
+INSERT INTO brintest_bloom SELECT
+ repeat(stringu1, 8)::bytea,
+ substr(stringu1, 1, 1)::"char",
+ stringu1::name, 142857 * tenthous,
+ thousand,
+ twothousand,
+ repeat(stringu1, 8),
+ unique1::oid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4/24' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ substr(stringu1, 1, 1)::bpchar,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20+02' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 100;
+-- throw in some NULL's and different values
+INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT
+ inet 'fe80::6e40:8ff:fea9:8c46' + tenthous,
+ cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous
+FROM tenk1 ORDER BY thousand, tenthous LIMIT 25;
+-- test bloom specific index options
+-- ndistinct must be >= -1.0
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(n_distinct_per_range = -1.1)
+);
+ERROR: value -1.1 out of bounds for option "n_distinct_per_range"
+DETAIL: Valid values are between "-1.000000" and "2147483647.000000".
+-- false_positive_rate must be between 0.001 and 1.0
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(false_positive_rate = 0.0)
+);
+ERROR: value 0.0 out of bounds for option "false_positive_rate"
+DETAIL: Valid values are between "0.001000" and "1.000000".
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(false_positive_rate = 1.01)
+);
+ERROR: value 1.01 out of bounds for option "false_positive_rate"
+DETAIL: Valid values are between "0.001000" and "1.000000".
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops,
+ charcol char_bloom_ops,
+ namecol name_bloom_ops,
+ int8col int8_bloom_ops,
+ int2col int2_bloom_ops,
+ int4col int4_bloom_ops,
+ textcol text_bloom_ops,
+ oidcol oid_bloom_ops,
+ float4col float4_bloom_ops,
+ float8col float8_bloom_ops,
+ macaddrcol macaddr_bloom_ops,
+ inetcol inet_bloom_ops,
+ cidrcol inet_bloom_ops,
+ bpcharcol bpchar_bloom_ops,
+ datecol date_bloom_ops,
+ timecol time_bloom_ops,
+ timestampcol timestamp_bloom_ops,
+ timestamptzcol timestamptz_bloom_ops,
+ intervalcol interval_bloom_ops,
+ timetzcol timetz_bloom_ops,
+ numericcol numeric_bloom_ops,
+ uuidcol uuid_bloom_ops,
+ lsncol pg_lsn_bloom_ops
+) with (pages_per_range = 1);
+CREATE TABLE brinopers_bloom (colname name, typ text,
+ op text[], value text[], matches int[],
+ check (cardinality(op) = cardinality(value)),
+ check (cardinality(op) = cardinality(matches)));
+INSERT INTO brinopers_bloom VALUES
+ ('byteacol', 'bytea',
+ '{=}',
+ '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}',
+ '{1}'),
+ ('charcol', '"char"',
+ '{=}',
+ '{M}',
+ '{6}'),
+ ('namecol', 'name',
+ '{=}',
+ '{MAAAAA}',
+ '{2}'),
+ ('int2col', 'int2',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int2col', 'int4',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int2col', 'int8',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int2',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int4',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int8',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int8col', 'int8',
+ '{=}',
+ '{1257141600}',
+ '{1}'),
+ ('textcol', 'text',
+ '{=}',
+ '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}',
+ '{1}'),
+ ('oidcol', 'oid',
+ '{=}',
+ '{8800}',
+ '{1}'),
+ ('float4col', 'float4',
+ '{=}',
+ '{1}',
+ '{4}'),
+ ('float4col', 'float8',
+ '{=}',
+ '{1}',
+ '{4}'),
+ ('float8col', 'float4',
+ '{=}',
+ '{0}',
+ '{1}'),
+ ('float8col', 'float8',
+ '{=}',
+ '{0}',
+ '{1}'),
+ ('macaddrcol', 'macaddr',
+ '{=}',
+ '{2c:00:2d:00:16:00}',
+ '{2}'),
+ ('inetcol', 'inet',
+ '{=}',
+ '{10.2.14.231/24}',
+ '{1}'),
+ ('inetcol', 'cidr',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('cidrcol', 'inet',
+ '{=}',
+ '{10.2.14/24}',
+ '{2}'),
+ ('cidrcol', 'inet',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('cidrcol', 'cidr',
+ '{=}',
+ '{10.2.14/24}',
+ '{2}'),
+ ('cidrcol', 'cidr',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('bpcharcol', 'bpchar',
+ '{=}',
+ '{W}',
+ '{6}'),
+ ('datecol', 'date',
+ '{=}',
+ '{2009-12-01}',
+ '{1}'),
+ ('timecol', 'time',
+ '{=}',
+ '{02:28:57}',
+ '{1}'),
+ ('timestampcol', 'timestamp',
+ '{=}',
+ '{1964-03-24 19:26:45}',
+ '{1}'),
+ ('timestampcol', 'timestamptz',
+ '{=}',
+ '{1964-03-24 19:26:45}',
+ '{1}'),
+ ('timestamptzcol', 'timestamptz',
+ '{=}',
+ '{1972-10-19 09:00:00-07}',
+ '{1}'),
+ ('intervalcol', 'interval',
+ '{=}',
+ '{1 mons 13 days 12:24}',
+ '{1}'),
+ ('timetzcol', 'timetz',
+ '{=}',
+ '{01:35:50+02}',
+ '{2}'),
+ ('numericcol', 'numeric',
+ '{=}',
+ '{2268164.347826086956521739130434782609}',
+ '{1}'),
+ ('uuidcol', 'uuid',
+ '{=}',
+ '{52225222-5222-5222-5222-522252225222}',
+ '{1}'),
+ ('lsncol', 'pg_lsn',
+ '{=, IS, IS NOT}',
+ '{44/455222, NULL, NULL}',
+ '{1, 25, 100}');
+DO $x$
+DECLARE
+ r record;
+ r2 record;
+ cond text;
+ idx_ctids tid[];
+ ss_ctids tid[];
+ count int;
+ plan_ok bool;
+ plan_line text;
+BEGIN
+ FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP
+
+ -- prepare the condition
+ IF r.value IS NULL THEN
+ cond := format('%I %s %L', r.colname, r.oper, r.value);
+ ELSE
+ cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ);
+ END IF;
+
+ -- run the query using the brin index
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get bitmap indexscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond)
+ INTO idx_ctids;
+
+ -- run the query using a seqscan
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get seqscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond)
+ INTO ss_ctids;
+
+ -- make sure both return the same results
+ count := array_length(idx_ctids, 1);
+
+ IF NOT (count = array_length(ss_ctids, 1) AND
+ idx_ctids @> ss_ctids AND
+ idx_ctids <@ ss_ctids) THEN
+ -- report the results of each scan to make the differences obvious
+ RAISE WARNING 'something not right in %: count %', r, count;
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP
+ RAISE NOTICE 'seqscan: %', r2;
+ END LOOP;
+
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP
+ RAISE NOTICE 'bitmapscan: %', r2;
+ END LOOP;
+ END IF;
+
+ -- make sure we found expected number of matches
+ IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF;
+ END LOOP;
+END;
+$x$;
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+INSERT INTO brintest_bloom SELECT
+ repeat(stringu1, 42)::bytea,
+ substr(stringu1, 1, 1)::"char",
+ stringu1::name, 142857 * tenthous,
+ thousand,
+ twothousand,
+ repeat(stringu1, 42),
+ unique1::oid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ substr(stringu1, 1, 1)::bpchar,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5;
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+VACUUM brintest_bloom; -- force a summarization cycle in brinidx
+UPDATE brintest_bloom SET int8col = int8col * int4col;
+UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL;
+-- Tests for brin_summarize_new_values
+SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index
+ERROR: "brintest_bloom" is not an index
+SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
+ERROR: "tenk1_unique1" is not a BRIN index
+SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected
+ brin_summarize_new_values
+---------------------------
+ 0
+(1 row)
+
+-- Tests for brin_desummarize_range
+SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range
+ERROR: block number out of range: -1
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+SELECT brin_desummarize_range('brinidx_bloom', 100000000);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+-- Test brin_summarize_range
+CREATE TABLE brin_summarize_bloom (
+ value int
+) WITH (fillfactor=10, autovacuum_enabled=false);
+CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2);
+-- Fill a few pages
+DO $$
+DECLARE curtid tid;
+BEGIN
+ LOOP
+ INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid;
+ EXIT WHEN curtid > tid '(2, 0)';
+ END LOOP;
+END;
+$$;
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 0);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- nothing: already summarized
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 1);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 2);
+ brin_summarize_range
+----------------------
+ 1
+(1 row)
+
+-- nothing: page doesn't exist in table
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- invalid block number values
+SELECT brin_summarize_range('brin_summarize_bloom_idx', -1);
+ERROR: block number out of range: -1
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296);
+ERROR: block number out of range: 4294967296
+-- test brin cost estimates behave sanely based on correlation of values
+CREATE TABLE brin_test_bloom (a INT, b INT);
+INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x);
+CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2);
+CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2);
+VACUUM ANALYZE brin_test_bloom;
+-- Ensure brin index is used when columns are perfectly correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1;
+ QUERY PLAN
+--------------------------------------------------
+ Bitmap Heap Scan on brin_test_bloom
+ Recheck Cond: (a = 1)
+ -> Bitmap Index Scan on brin_test_bloom_a_idx
+ Index Cond: (a = 1)
+(4 rows)
+
+-- Ensure brin index is not used when values are not correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1;
+ QUERY PLAN
+-----------------------------
+ Seq Scan on brin_test_bloom
+ Filter: (b = 1)
+(2 rows)
+
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 1b3c146e4c..dca8e9eb34 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2034,6 +2034,7 @@ ORDER BY 1, 2, 3;
2742 | 16 | @@
3580 | 1 | <
3580 | 1 | <<
+ 3580 | 1 | =
3580 | 2 | &<
3580 | 2 | <=
3580 | 3 | &&
@@ -2097,7 +2098,7 @@ ORDER BY 1, 2, 3;
4000 | 26 | >>
4000 | 27 | >>=
4000 | 28 | ^@
-(125 rows)
+(126 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index daac0ff49d..72c7598c89 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4989,8 +4989,9 @@ List of access methods
List of operator classes
AM | Input type | Storage type | Operator class | Default?
------+------------+--------------+----------------+----------
+ brin | oid | | oid_bloom_ops | no
brin | oid | | oid_minmax_ops | yes
-(1 row)
+(2 rows)
\dAf spgist
List of operator families
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 274130e706..97bf9797de 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -67,13 +67,14 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%'
(SELECT 1 FROM pg_type as p2
WHERE p2.typname = ('_' || p1.typname)::name AND
p2.typelem = p1.oid and p1.typarray = p2.oid);
- oid | typname
-------+-----------------
+ oid | typname
+------+-----------------------
194 | pg_node_tree
3361 | pg_ndistinct
3402 | pg_dependencies
5017 | pg_mcv_list
-(4 rows)
+ 9034 | pg_brin_bloom_summary
+(5 rows)
-- Make sure typarray points to a varlena array type of our own base
SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 026ea880cd..b49239b1d0 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -75,6 +75,11 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi
# ----------
test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash
+# ----------
+# Additional BRIN tests
+# ----------
+test: brin_bloom
+
# ----------
# Another group of parallel tests
# ----------
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 979d926119..abce8e180a 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -107,6 +107,7 @@ test: delete
test: namespace
test: prepared_xacts
test: brin
+test: brin_bloom
test: gin
test: gist
test: spgist
diff --git a/src/test/regress/sql/brin_bloom.sql b/src/test/regress/sql/brin_bloom.sql
new file mode 100644
index 0000000000..abd5a33deb
--- /dev/null
+++ b/src/test/regress/sql/brin_bloom.sql
@@ -0,0 +1,404 @@
+CREATE TABLE brintest_bloom (byteacol bytea,
+ charcol "char",
+ namecol name,
+ int8col bigint,
+ int2col smallint,
+ int4col integer,
+ textcol text,
+ oidcol oid,
+ float4col real,
+ float8col double precision,
+ macaddrcol macaddr,
+ inetcol inet,
+ cidrcol cidr,
+ bpcharcol character,
+ datecol date,
+ timecol time without time zone,
+ timestampcol timestamp without time zone,
+ timestamptzcol timestamp with time zone,
+ intervalcol interval,
+ timetzcol time with time zone,
+ numericcol numeric,
+ uuidcol uuid,
+ lsncol pg_lsn
+) WITH (fillfactor=10);
+
+INSERT INTO brintest_bloom SELECT
+ repeat(stringu1, 8)::bytea,
+ substr(stringu1, 1, 1)::"char",
+ stringu1::name, 142857 * tenthous,
+ thousand,
+ twothousand,
+ repeat(stringu1, 8),
+ unique1::oid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4/24' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ substr(stringu1, 1, 1)::bpchar,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20+02' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 100;
+
+-- throw in some NULL's and different values
+INSERT INTO brintest_bloom (inetcol, cidrcol) SELECT
+ inet 'fe80::6e40:8ff:fea9:8c46' + tenthous,
+ cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous
+FROM tenk1 ORDER BY thousand, tenthous LIMIT 25;
+
+-- test bloom specific index options
+-- ndistinct must be >= -1.0
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(n_distinct_per_range = -1.1)
+);
+-- false_positive_rate must be between 0.001 and 1.0
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(false_positive_rate = 0.0)
+);
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops(false_positive_rate = 1.01)
+);
+
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+ byteacol bytea_bloom_ops,
+ charcol char_bloom_ops,
+ namecol name_bloom_ops,
+ int8col int8_bloom_ops,
+ int2col int2_bloom_ops,
+ int4col int4_bloom_ops,
+ textcol text_bloom_ops,
+ oidcol oid_bloom_ops,
+ float4col float4_bloom_ops,
+ float8col float8_bloom_ops,
+ macaddrcol macaddr_bloom_ops,
+ inetcol inet_bloom_ops,
+ cidrcol inet_bloom_ops,
+ bpcharcol bpchar_bloom_ops,
+ datecol date_bloom_ops,
+ timecol time_bloom_ops,
+ timestampcol timestamp_bloom_ops,
+ timestamptzcol timestamptz_bloom_ops,
+ intervalcol interval_bloom_ops,
+ timetzcol timetz_bloom_ops,
+ numericcol numeric_bloom_ops,
+ uuidcol uuid_bloom_ops,
+ lsncol pg_lsn_bloom_ops
+) with (pages_per_range = 1);
+
+CREATE TABLE brinopers_bloom (colname name, typ text,
+ op text[], value text[], matches int[],
+ check (cardinality(op) = cardinality(value)),
+ check (cardinality(op) = cardinality(matches)));
+
+INSERT INTO brinopers_bloom VALUES
+ ('byteacol', 'bytea',
+ '{=}',
+ '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}',
+ '{1}'),
+ ('charcol', '"char"',
+ '{=}',
+ '{M}',
+ '{6}'),
+ ('namecol', 'name',
+ '{=}',
+ '{MAAAAA}',
+ '{2}'),
+ ('int2col', 'int2',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int2col', 'int4',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int2col', 'int8',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int2',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int4',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int4col', 'int8',
+ '{=}',
+ '{800}',
+ '{1}'),
+ ('int8col', 'int8',
+ '{=}',
+ '{1257141600}',
+ '{1}'),
+ ('textcol', 'text',
+ '{=}',
+ '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}',
+ '{1}'),
+ ('oidcol', 'oid',
+ '{=}',
+ '{8800}',
+ '{1}'),
+ ('float4col', 'float4',
+ '{=}',
+ '{1}',
+ '{4}'),
+ ('float4col', 'float8',
+ '{=}',
+ '{1}',
+ '{4}'),
+ ('float8col', 'float4',
+ '{=}',
+ '{0}',
+ '{1}'),
+ ('float8col', 'float8',
+ '{=}',
+ '{0}',
+ '{1}'),
+ ('macaddrcol', 'macaddr',
+ '{=}',
+ '{2c:00:2d:00:16:00}',
+ '{2}'),
+ ('inetcol', 'inet',
+ '{=}',
+ '{10.2.14.231/24}',
+ '{1}'),
+ ('inetcol', 'cidr',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('cidrcol', 'inet',
+ '{=}',
+ '{10.2.14/24}',
+ '{2}'),
+ ('cidrcol', 'inet',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('cidrcol', 'cidr',
+ '{=}',
+ '{10.2.14/24}',
+ '{2}'),
+ ('cidrcol', 'cidr',
+ '{=}',
+ '{fe80::6e40:8ff:fea9:8c46}',
+ '{1}'),
+ ('bpcharcol', 'bpchar',
+ '{=}',
+ '{W}',
+ '{6}'),
+ ('datecol', 'date',
+ '{=}',
+ '{2009-12-01}',
+ '{1}'),
+ ('timecol', 'time',
+ '{=}',
+ '{02:28:57}',
+ '{1}'),
+ ('timestampcol', 'timestamp',
+ '{=}',
+ '{1964-03-24 19:26:45}',
+ '{1}'),
+ ('timestampcol', 'timestamptz',
+ '{=}',
+ '{1964-03-24 19:26:45}',
+ '{1}'),
+ ('timestamptzcol', 'timestamptz',
+ '{=}',
+ '{1972-10-19 09:00:00-07}',
+ '{1}'),
+ ('intervalcol', 'interval',
+ '{=}',
+ '{1 mons 13 days 12:24}',
+ '{1}'),
+ ('timetzcol', 'timetz',
+ '{=}',
+ '{01:35:50+02}',
+ '{2}'),
+ ('numericcol', 'numeric',
+ '{=}',
+ '{2268164.347826086956521739130434782609}',
+ '{1}'),
+ ('uuidcol', 'uuid',
+ '{=}',
+ '{52225222-5222-5222-5222-522252225222}',
+ '{1}'),
+ ('lsncol', 'pg_lsn',
+ '{=, IS, IS NOT}',
+ '{44/455222, NULL, NULL}',
+ '{1, 25, 100}');
+
+DO $x$
+DECLARE
+ r record;
+ r2 record;
+ cond text;
+ idx_ctids tid[];
+ ss_ctids tid[];
+ count int;
+ plan_ok bool;
+ plan_line text;
+BEGIN
+ FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_bloom, unnest(op) WITH ORDINALITY AS oper LOOP
+
+ -- prepare the condition
+ IF r.value IS NULL THEN
+ cond := format('%I %s %L', r.colname, r.oper, r.value);
+ ELSE
+ cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ);
+ END IF;
+
+ -- run the query using the brin index
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Bitmap Heap Scan on brintest_bloom%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get bitmap indexscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond)
+ INTO idx_ctids;
+
+ -- run the query using a seqscan
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Seq Scan on brintest_bloom%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get seqscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_bloom WHERE %s $y$, cond)
+ INTO ss_ctids;
+
+ -- make sure both return the same results
+ count := array_length(idx_ctids, 1);
+
+ IF NOT (count = array_length(ss_ctids, 1) AND
+ idx_ctids @> ss_ctids AND
+ idx_ctids <@ ss_ctids) THEN
+ -- report the results of each scan to make the differences obvious
+ RAISE WARNING 'something not right in %: count %', r, count;
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP
+ RAISE NOTICE 'seqscan: %', r2;
+ END LOOP;
+
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_bloom WHERE ' || cond LOOP
+ RAISE NOTICE 'bitmapscan: %', r2;
+ END LOOP;
+ END IF;
+
+ -- make sure we found expected number of matches
+ IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF;
+ END LOOP;
+END;
+$x$;
+
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+
+INSERT INTO brintest_bloom SELECT
+ repeat(stringu1, 42)::bytea,
+ substr(stringu1, 1, 1)::"char",
+ stringu1::name, 142857 * tenthous,
+ thousand,
+ twothousand,
+ repeat(stringu1, 42),
+ unique1::oid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ substr(stringu1, 1, 1)::bpchar,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5;
+
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+VACUUM brintest_bloom; -- force a summarization cycle in brinidx
+
+UPDATE brintest_bloom SET int8col = int8col * int4col;
+UPDATE brintest_bloom SET textcol = '' WHERE textcol IS NOT NULL;
+
+-- Tests for brin_summarize_new_values
+SELECT brin_summarize_new_values('brintest_bloom'); -- error, not an index
+SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
+SELECT brin_summarize_new_values('brinidx_bloom'); -- ok, no change expected
+
+-- Tests for brin_desummarize_range
+SELECT brin_desummarize_range('brinidx_bloom', -1); -- error, invalid range
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+SELECT brin_desummarize_range('brinidx_bloom', 0);
+SELECT brin_desummarize_range('brinidx_bloom', 100000000);
+
+-- Test brin_summarize_range
+CREATE TABLE brin_summarize_bloom (
+ value int
+) WITH (fillfactor=10, autovacuum_enabled=false);
+CREATE INDEX brin_summarize_bloom_idx ON brin_summarize_bloom USING brin (value) WITH (pages_per_range=2);
+-- Fill a few pages
+DO $$
+DECLARE curtid tid;
+BEGIN
+ LOOP
+ INSERT INTO brin_summarize_bloom VALUES (1) RETURNING ctid INTO curtid;
+ EXIT WHEN curtid > tid '(2, 0)';
+ END LOOP;
+END;
+$$;
+
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 0);
+-- nothing: already summarized
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 1);
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 2);
+-- nothing: page doesn't exist in table
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967295);
+-- invalid block number values
+SELECT brin_summarize_range('brin_summarize_bloom_idx', -1);
+SELECT brin_summarize_range('brin_summarize_bloom_idx', 4294967296);
+
+
+-- test brin cost estimates behave sanely based on correlation of values
+CREATE TABLE brin_test_bloom (a INT, b INT);
+INSERT INTO brin_test_bloom SELECT x/100,x%100 FROM generate_series(1,10000) x(x);
+CREATE INDEX brin_test_bloom_a_idx ON brin_test_bloom USING brin (a) WITH (pages_per_range = 2);
+CREATE INDEX brin_test_bloom_b_idx ON brin_test_bloom USING brin (b) WITH (pages_per_range = 2);
+VACUUM ANALYZE brin_test_bloom;
+
+-- Ensure brin index is used when columns are perfectly correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE a = 1;
+-- Ensure brin index is not used when values are not correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_bloom WHERE b = 1;
--
2.25.4
[text/plain] 0004-BRIN-minmax-multi-indexes-20200912.patch (217.3K, ../20200912161303.z54jctx5dw4srrh3@development/5-0004-BRIN-minmax-multi-indexes-20200912.patch)
download | inline diff:
From 87f5f02c4c0df16715e7ca5064f22bc6c1e64f8c Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 12 Sep 2020 15:07:49 +0200
Subject: [PATCH 4/5] BRIN minmax-multi indexes
Adds a BRIN minmax-multi opclass, summarizing each range into a list of
intervals, allowing more efficient handling of cases where the minmax
opclasses quickly degrade.
Instead of representing each range with a single interval, minmax-multi
opclasses store a list of intervals and points. This allows effective
handling of outliers and poorly correlated values.
The number of intervals/points per range may be configured using an
opclass parameter values_per_range. When building the summary, the
interval are merged based on distance, determined by the new support
BRIN procedure.
Author: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Reviewed-by: Alvaro Herrera <alvherre@2ndquadrant.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com
---
doc/src/sgml/brin.sgml | 252 +-
doc/src/sgml/ref/create_index.sgml | 11 +
src/backend/access/brin/Makefile | 1 +
src/backend/access/brin/brin_minmax_multi.c | 2390 +++++++++++++++++++
src/backend/access/brin/brin_tuple.c | 17 +
src/include/access/brin.h | 1 +
src/include/access/brin_internal.h | 3 +
src/include/access/brin_tuple.h | 4 +
src/include/access/transam.h | 2 +-
src/include/catalog/pg_amop.dat | 544 +++++
src/include/catalog/pg_amproc.dat | 600 ++++-
src/include/catalog/pg_opclass.dat | 57 +
src/include/catalog/pg_opfamily.dat | 28 +
src/include/catalog/pg_proc.dat | 80 +
src/include/catalog/pg_type.dat | 7 +
src/test/regress/expected/brin_multi.out | 421 ++++
src/test/regress/expected/psql.out | 13 +-
src/test/regress/expected/type_sanity.out | 7 +-
src/test/regress/parallel_schedule | 2 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/brin_multi.sql | 371 +++
21 files changed, 4793 insertions(+), 19 deletions(-)
create mode 100644 src/backend/access/brin/brin_minmax_multi.c
create mode 100644 src/test/regress/expected/brin_multi.out
create mode 100644 src/test/regress/sql/brin_multi.sql
diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml
index 577dd18c49..3caa30f104 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (date,date)</literal></entry></row>
<row><entry><literal>>= (date,date)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry>
+ <entry><literal>= (date,date)</literal></entry>
+ </row>
+ <row><entry><literal>< (date,date)</literal></entry></row>
+ <row><entry><literal><= (date,date)</literal></entry></row>
+ <row><entry><literal>> (date,date)</literal></entry></row>
+ <row><entry><literal>>= (date,date)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>float4_bloom_ops</literal></entry>
<entry><literal>= (float4,float4)</literal></entry>
@@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (float4,float4)</literal></entry></row>
<row><entry><literal>>= (float4,float4)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry>
+ <entry><literal>= (float4,float4)</literal></entry>
+ </row>
+ <row><entry><literal>< (float4,float4)</literal></entry></row>
+ <row><entry><literal>> (float4,float4)</literal></entry></row>
+ <row><entry><literal><= (float4,float4)</literal></entry></row>
+ <row><entry><literal>>= (float4,float4)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>float8_bloom_ops</literal></entry>
<entry><literal>= (float8,float8)</literal></entry>
@@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (float8,float8)</literal></entry></row>
<row><entry><literal>>= (float8,float8)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry>
+ <entry><literal>= (float8,float8)</literal></entry>
+ </row>
+ <row><entry><literal>< (float8,float8)</literal></entry></row>
+ <row><entry><literal><= (float8,float8)</literal></entry></row>
+ <row><entry><literal>> (float8,float8)</literal></entry></row>
+ <row><entry><literal>>= (float8,float8)</literal></entry></row>
+
<row>
<entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry>
<entry><literal><< (inet,inet)</literal></entry>
@@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (inet,inet)</literal></entry></row>
<row><entry><literal>>= (inet,inet)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry>
+ <entry><literal>= (inet,inet)</literal></entry>
+ </row>
+ <row><entry><literal>< (inet,inet)</literal></entry></row>
+ <row><entry><literal><= (inet,inet)</literal></entry></row>
+ <row><entry><literal>> (inet,inet)</literal></entry></row>
+ <row><entry><literal>>= (inet,inet)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>int2_bloom_ops</literal></entry>
<entry><literal>= (int2,int2)</literal></entry>
@@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (int2,int2)</literal></entry></row>
<row><entry><literal>>= (int2,int2)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry>
+ <entry><literal>= (int2,int2)</literal></entry>
+ </row>
+ <row><entry><literal>< (int2,int2)</literal></entry></row>
+ <row><entry><literal>> (int2,int2)</literal></entry></row>
+ <row><entry><literal><= (int2,int2)</literal></entry></row>
+ <row><entry><literal>>= (int2,int2)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>int4_bloom_ops</literal></entry>
<entry><literal>= (int4,int4)</literal></entry>
@@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (int4,int4)</literal></entry></row>
<row><entry><literal>>= (int4,int4)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry>
+ <entry><literal>= (int4,int4)</literal></entry>
+ </row>
+ <row><entry><literal>< (int4,int4)</literal></entry></row>
+ <row><entry><literal>> (int4,int4)</literal></entry></row>
+ <row><entry><literal><= (int4,int4)</literal></entry></row>
+ <row><entry><literal>>= (int4,int4)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>int8_bloom_ops</literal></entry>
<entry><literal>= (bigint,bigint)</literal></entry>
@@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (bigint,bigint)</literal></entry></row>
<row><entry><literal>>= (bigint,bigint)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry>
+ <entry><literal>= (bigint,bigint)</literal></entry>
+ </row>
+ <row><entry><literal>< (bigint,bigint)</literal></entry></row>
+ <row><entry><literal>> (bigint,bigint)</literal></entry></row>
+ <row><entry><literal><= (bigint,bigint)</literal></entry></row>
+ <row><entry><literal>>= (bigint,bigint)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>interval_bloom_ops</literal></entry>
<entry><literal>= (interval,interval)</literal></entry>
@@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (interval,interval)</literal></entry></row>
<row><entry><literal>>= (interval,interval)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry>
+ <entry><literal>= (interval,interval)</literal></entry>
+ </row>
+ <row><entry><literal>< (interval,interval)</literal></entry></row>
+ <row><entry><literal><= (interval,interval)</literal></entry></row>
+ <row><entry><literal>> (interval,interval)</literal></entry></row>
+ <row><entry><literal>>= (interval,interval)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>macaddr_bloom_ops</literal></entry>
<entry><literal>= (macaddr,macaddr)</literal></entry>
@@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (macaddr,macaddr)</literal></entry></row>
<row><entry><literal>>= (macaddr,macaddr)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry>
+ <entry><literal>= (macaddr,macaddr)</literal></entry>
+ </row>
+ <row><entry><literal>< (macaddr,macaddr)</literal></entry></row>
+ <row><entry><literal><= (macaddr,macaddr)</literal></entry></row>
+ <row><entry><literal>> (macaddr,macaddr)</literal></entry></row>
+ <row><entry><literal>>= (macaddr,macaddr)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry>
<entry><literal>= (macaddr8,macaddr8)</literal></entry>
@@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (macaddr8,macaddr8)</literal></entry></row>
<row><entry><literal>>= (macaddr8,macaddr8)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry>
+ <entry><literal>= (macaddr8,macaddr8)</literal></entry>
+ </row>
+ <row><entry><literal>< (macaddr8,macaddr8)</literal></entry></row>
+ <row><entry><literal><= (macaddr8,macaddr8)</literal></entry></row>
+ <row><entry><literal>> (macaddr8,macaddr8)</literal></entry></row>
+ <row><entry><literal>>= (macaddr8,macaddr8)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>name_bloom_ops</literal></entry>
<entry><literal>= (name,name)</literal></entry>
@@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (numeric,numeric)</literal></entry></row>
<row><entry><literal>>= (numeric,numeric)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry>
+ <entry><literal>= (numeric,numeric)</literal></entry>
+ </row>
+ <row><entry><literal>< (numeric,numeric)</literal></entry></row>
+ <row><entry><literal><= (numeric,numeric)</literal></entry></row>
+ <row><entry><literal>> (numeric,numeric)</literal></entry></row>
+ <row><entry><literal>>= (numeric,numeric)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>oid_bloom_ops</literal></entry>
<entry><literal>= (oid,oid)</literal></entry>
@@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (oid,oid)</literal></entry></row>
<row><entry><literal>>= (oid,oid)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry>
+ <entry><literal>= (oid,oid)</literal></entry>
+ </row>
+ <row><entry><literal>< (oid,oid)</literal></entry></row>
+ <row><entry><literal>> (oid,oid)</literal></entry></row>
+ <row><entry><literal><= (oid,oid)</literal></entry></row>
+ <row><entry><literal>>= (oid,oid)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry>
<entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
@@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (pg_lsn,pg_lsn)</literal></entry></row>
<row><entry><literal>>= (pg_lsn,pg_lsn)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry>
+ <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
+ </row>
+ <row><entry><literal>< (pg_lsn,pg_lsn)</literal></entry></row>
+ <row><entry><literal>> (pg_lsn,pg_lsn)</literal></entry></row>
+ <row><entry><literal><= (pg_lsn,pg_lsn)</literal></entry></row>
+ <row><entry><literal>>= (pg_lsn,pg_lsn)</literal></entry></row>
+
<row>
<entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry>
<entry><literal>= (anyrange,anyrange)</literal></entry>
@@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (tid,tid)</literal></entry></row>
<row><entry><literal>>= (tid,tid)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry>
+ <entry><literal>= (tid,tid)</literal></entry>
+ </row>
+ <row><entry><literal>< (tid,tid)</literal></entry></row>
+ <row><entry><literal>> (tid,tid)</literal></entry></row>
+ <row><entry><literal><= (tid,tid)</literal></entry></row>
+ <row><entry><literal>>= (tid,tid)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>timestamp_bloom_ops</literal></entry>
<entry><literal>= (timestamp,timestamp)</literal></entry>
@@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timestamp,timestamp)</literal></entry></row>
<row><entry><literal>>= (timestamp,timestamp)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry>
+ <entry><literal>= (timestamp,timestamp)</literal></entry>
+ </row>
+ <row><entry><literal>< (timestamp,timestamp)</literal></entry></row>
+ <row><entry><literal><= (timestamp,timestamp)</literal></entry></row>
+ <row><entry><literal>> (timestamp,timestamp)</literal></entry></row>
+ <row><entry><literal>>= (timestamp,timestamp)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry>
<entry><literal>= (timestamptz,timestamptz)</literal></entry>
@@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timestamptz,timestamptz)</literal></entry></row>
<row><entry><literal>>= (timestamptz,timestamptz)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry>
+ <entry><literal>= (timestamptz,timestamptz)</literal></entry>
+ </row>
+ <row><entry><literal>< (timestamptz,timestamptz)</literal></entry></row>
+ <row><entry><literal><= (timestamptz,timestamptz)</literal></entry></row>
+ <row><entry><literal>> (timestamptz,timestamptz)</literal></entry></row>
+ <row><entry><literal>>= (timestamptz,timestamptz)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>time_bloom_ops</literal></entry>
<entry><literal>= (time,time)</literal></entry>
@@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (time,time)</literal></entry></row>
<row><entry><literal>>= (time,time)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry>
+ <entry><literal>= (time,time)</literal></entry>
+ </row>
+ <row><entry><literal>< (time,time)</literal></entry></row>
+ <row><entry><literal><= (time,time)</literal></entry></row>
+ <row><entry><literal>> (time,time)</literal></entry></row>
+ <row><entry><literal>>= (time,time)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>timetz_bloom_ops</literal></entry>
<entry><literal>= (timetz,timetz)</literal></entry>
@@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timetz,timetz)</literal></entry></row>
<row><entry><literal>>= (timetz,timetz)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry>
+ <entry><literal>= (timetz,timetz)</literal></entry>
+ </row>
+ <row><entry><literal>< (timetz,timetz)</literal></entry></row>
+ <row><entry><literal><= (timetz,timetz)</literal></entry></row>
+ <row><entry><literal>> (timetz,timetz)</literal></entry></row>
+ <row><entry><literal>>= (timetz,timetz)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>uuid_bloom_ops</literal></entry>
<entry><literal>= (uuid,uuid)</literal></entry>
@@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (uuid,uuid)</literal></entry></row>
<row><entry><literal>>= (uuid,uuid)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry>
+ <entry><literal>= (uuid,uuid)</literal></entry>
+ </row>
+ <row><entry><literal>< (uuid,uuid)</literal></entry></row>
+ <row><entry><literal>> (uuid,uuid)</literal></entry></row>
+ <row><entry><literal><= (uuid,uuid)</literal></entry></row>
+ <row><entry><literal>>= (uuid,uuid)</literal></entry></row>
+
<row>
<entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry>
<entry><literal>= (varbit,varbit)</literal></entry>
@@ -654,13 +825,14 @@ typedef struct BrinOpcInfo
</varlistentry>
</variablelist>
- The core distribution includes support for two types of operator classes:
- minmax and inclusion. Operator class definitions using them are shipped for
- in-core data types as appropriate. Additional operator classes can be
- defined by the user for other data types using equivalent definitions,
- without having to write any source code; appropriate catalog entries being
- declared is enough. Note that assumptions about the semantics of operator
- strategies are embedded in the support functions' source code.
+ The core distribution includes support for four types of operator classes:
+ minmax, multi-minmax, inclusion and bloom. Operator class definitions
+ using them are shipped for in-core data types as appropriate. Additional
+ operator classes can be defined by the user for other data types using
+ equivalent definitions, without having to write any source code;
+ appropriate catalog entries being declared is enough. Note that
+ assumptions about the semantics of operator strategies are embedded in the
+ support functions' source code.
</para>
<para>
@@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo
and return a hash of the value.
</para>
+ <para>
+ The multi-minmax operator class is also intended for data types implementing
+ a totally ordered sets, and may be seen as a simple extension of the minmax
+ operator class. While minmax operator class summarizes values from each block
+ range into a single contiguous interval, multi-minmax allows summarization
+ into multiple smaller intervals to improve handling of outlier values.
+ It is possible to use the multi-minmax support procedures alongside the
+ corresponding operators, as shown in
+ <xref linkend="brin-extensibility-multi-minmax-table"/>.
+ All operator class members (procedures and operators) are mandatory.
+ </para>
+
+ <table id="brin-extensibility-multi-minmax-table">
+ <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Operator class member</entry>
+ <entry>Object</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Support Procedure 1</entry>
+ <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 2</entry>
+ <entry>internal function <function>brin_minmax_multi_add_value()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 3</entry>
+ <entry>internal function <function>brin_minmax_multi_consistent()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 4</entry>
+ <entry>internal function <function>brin_minmax_multi_union()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 11</entry>
+ <entry>function to compute distance between two values (length of a range)</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 1</entry>
+ <entry>operator less-than</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 2</entry>
+ <entry>operator less-than-or-equal-to</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 3</entry>
+ <entry>operator equal-to</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 4</entry>
+ <entry>operator greater-than-or-equal-to</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 5</entry>
+ <entry>operator greater-than</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
<para>
Both minmax and inclusion operator classes support cross-data-type
operators, though with these the dependencies become more complicated.
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 9c90d451a7..d86e3ed8f2 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -586,6 +586,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>values_per_range</literal></term>
+ <listitem>
+ <para>
+ Defines the maximum number of values stored by <acronym>BRIN</acronym>
+ minmax indexes to summarize a block range. Each value may represent
+ eiter a point, or boundary of an interval. Values must be be between
+ 16 and 256, and the default value is 32.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect2>
diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile
index 6b56131215..a386cb71f1 100644
--- a/src/backend/access/brin/Makefile
+++ b/src/backend/access/brin/Makefile
@@ -17,6 +17,7 @@ OBJS = \
brin_bloom.o \
brin_inclusion.o \
brin_minmax.o \
+ brin_minmax_multi.o \
brin_pageops.o \
brin_revmap.o \
brin_tuple.o \
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
new file mode 100644
index 0000000000..227927fcf5
--- /dev/null
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -0,0 +1,2390 @@
+/*
+ * brin_minmax_multi.c
+ * Implementation of Multi Min/Max opclass for BRIN
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * Implements a variant of minmax opclass, where the summary is composed of
+ * multiple smaller intervals. This allows us to handle outliers, which
+ * usually makes the simple minmax opclass inefficient.
+ *
+ * Consider for example page range with simple minmax interval [1000,2000],
+ * and assume a new row gets inserted into the range with value 1000000.
+ * Due to that the interval gets [1000,1000000]. I.e. the minmax interval
+ * got 1000x wider and won't be useful to eliminate scan keys between 2001
+ * and 1000000.
+ *
+ * With multi-minmax opclass, we may have [1000,2000] interval initially,
+ * but after adding the new row we start tracking it as two interval:
+ *
+ * [1000,2000] and [1000000,1000000]
+ *
+ * This allow us to still eliminate the page range when the scan keys hit
+ * the gap between 2000 and 1000000, making it useful in cases when the
+ * simple minmax opclass gets inefficient.
+ *
+ * The number of intervals tracked per page range is somewhat flexible.
+ * What is restricted is the number of values per page range, and the limit
+ * is currently 64 (see values_per_range reloption). Collapsed intervals
+ * (with equal minimum and maximum value) are stored as a single value,
+ * while regular intervals require two values.
+ *
+ * When the number of values gets too high (by adding new values to the
+ * summary), we merge some of the intervals to free space for more values.
+ * This is done in a greedy way - we simply pick the two closest intervals,
+ * merge them, and repeat this until the number of values to store gets
+ * sufficiently low (below 75% of maximum values), but that is mostly
+ * arbitrary threshold and may be changed easily).
+ *
+ * To pick the closest intervals we use the "distance" support procedure,
+ * which measures space between two ranges (i.e. length of an interval).
+ * The computed value may be an approximation - in the worst case we will
+ * merge two ranges that are slightly less optimal at that step, but the
+ * index should still produce correct results.
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/brin/brin_minmax_multi.c
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/brin.h"
+#include "access/brin_internal.h"
+#include "access/brin_tuple.h"
+#include "access/reloptions.h"
+#include "access/stratnum.h"
+#include "access/htup_details.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_amop.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/datum.h"
+#include "utils/inet.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/numeric.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+#include "utils/uuid.h"
+
+/*
+ * Additional SQL level support functions
+ *
+ * Procedure numbers must not use values reserved for BRIN itself; see
+ * brin_internal.h.
+ */
+#define MINMAX_MAX_PROCNUMS 1 /* maximum support procs we need */
+#define PROCNUM_DISTANCE 11 /* required, distance between values*/
+
+/*
+ * Subtract this from procnum to obtain index in MinmaxMultiOpaque arrays
+ * (Must be equal to minimum of private procnums).
+ */
+#define PROCNUM_BASE 11
+
+
+typedef struct MinmaxMultiOpaque
+{
+ FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS];
+ bool extra_proc_missing[MINMAX_MAX_PROCNUMS];
+ Oid cached_subtype;
+ FmgrInfo strategy_procinfos[BTMaxStrategyNumber];
+} MinmaxMultiOpaque;
+
+/*
+ * Storage type for BRIN's minmax reloptions
+ */
+typedef struct MinMaxOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int valuesPerRange; /* number of values per range */
+} MinMaxOptions;
+
+#define MINMAX_DEFAULT_VALUES_PER_PAGE 32
+
+#define MinMaxGetValuesPerRange(opts) \
+ ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \
+ ((MinMaxOptions *) (opts))->valuesPerRange : \
+ MINMAX_DEFAULT_VALUES_PER_PAGE)
+
+
+
+/*
+ * The summary of multi-minmax indexes has two representations - Ranges for
+ * convenient processing, and SerializedRanges for storage in bytea value.
+ *
+ * The Ranges struct stores the boundary values in a single array, but we
+ * treat regular and single-point ranges differently to save space. For
+ * regular ranges (with different boundary values) we have to store both
+ * values, while for "single-point ranges" we only need to save one value.
+ *
+ * The 'values' array stores boundary values for regular ranges first (there
+ * are 2*nranges values to store), and then the nvalues boundary values for
+ * single-point ranges. That is, we have (2*nranges + nvalues) boundary
+ * values in the array.
+ *
+ * +---------------------------------+-------------------------------+
+ * | ranges (sorted pairs of values) | sorted values (single points) |
+ * +---------------------------------+-------------------------------+
+ *
+ * This allows us to quickly add new values, and store outliers without
+ * making the other ranges very wide.
+ *
+ * We never store more than maxvalues values (as set by values_per_range
+ * reloption). If needed we merge some of the ranges.
+ *
+ * To minimize palloc overhead, we always allocate the full array with
+ * space for maxvalues elements. This should be fine as long as the
+ * maxvalues is reasonably small (64 seems fine), which is the case
+ * thanks to values_per_range reloption being limited to 256.
+ */
+typedef struct Ranges
+{
+ Oid typid;
+
+ /* (2*nranges + nvalues) <= maxvalues */
+ int nranges; /* number of ranges in the array (stored) */
+ int nvalues; /* number of values in the data array (all) */
+ int maxvalues; /* maximum number of values (reloption) */
+
+ /* values stored for this range - either raw values, or ranges */
+ Datum values[FLEXIBLE_ARRAY_MEMBER];
+} Ranges;
+
+/*
+ * On-disk the summary is stored as a bytea value, represented by the
+ * SerializedRanges structure. It has a 4B varlena header, so can treated
+ * as varlena directly.
+ *
+ * See range_serialize/range_deserialize methods for serialization details.
+ */
+typedef struct SerializedRanges
+{
+ /* varlena header (do not touch directly!) */
+ int32 vl_len_;
+
+ /* type of values stored in the data array */
+ Oid typid;
+
+ /* (2*nranges + nvalues) <= maxvalues */
+ int nranges; /* number of ranges in the array (stored) */
+ int nvalues; /* number of values in the data array (all) */
+ int maxvalues; /* maximum number of values (reloption) */
+
+ /* contains the actual data */
+ char data[FLEXIBLE_ARRAY_MEMBER];
+} SerializedRanges;
+
+static SerializedRanges *range_serialize(Ranges *range);
+
+static Ranges *range_deserialize(SerializedRanges *range);
+
+/* Cache for support and strategy procesures. */
+
+static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno,
+ uint16 procnum);
+
+static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc,
+ uint16 attno, Oid subtype, uint16 strategynum);
+
+
+/*
+ * minmax_multi_init
+ * Initialize the deserialized range list, allocate all the memory.
+ *
+ * This is only in-memory representation of the ranges, so we allocate
+ * everything enough space for the maximum number of values (so as not
+ * to have to do repallocs as the ranges grow).
+ */
+static Ranges *
+minmax_multi_init(int maxvalues)
+{
+ Size len;
+ Ranges * ranges;
+
+ Assert(maxvalues > 0);
+
+ len = offsetof(Ranges, values); /* fixed header */
+ len += maxvalues * sizeof(Datum); /* Datum values */
+
+ ranges = (Ranges *) palloc0(len);
+
+ ranges->maxvalues = maxvalues;
+
+ return ranges;
+}
+
+/*
+ * range_serialize
+ * Serialize the in-memory representation into a compact varlena value.
+ *
+ * Simply copy the header and then also the individual values, as stored
+ * in the in-memory value array.
+ */
+static SerializedRanges *
+range_serialize(Ranges *range)
+{
+ Size len;
+ int nvalues;
+ SerializedRanges *serialized;
+ Oid typid;
+ int typlen;
+ bool typbyval;
+
+ int i;
+ char *ptr;
+
+ /* simple sanity checks */
+ Assert(range->nranges >= 0);
+ Assert(range->nvalues >= 0);
+ Assert(range->maxvalues > 0);
+
+ /* see how many Datum values we actually have */
+ nvalues = 2*range->nranges + range->nvalues;
+
+ Assert(2*range->nranges + range->nvalues <= range->maxvalues);
+
+ typid = range->typid;
+ typbyval = get_typbyval(typid);
+ typlen = get_typlen(typid);
+
+ /* header is always needed */
+ len = offsetof(SerializedRanges,data);
+
+ /*
+ * The space needed depends on data type - for fixed-length data types
+ * (by-value and some by-reference) it's pretty simple, just multiply
+ * (attlen * nvalues) and we're done. For variable-length by-reference
+ * types we need to actually walk all the values and sum the lengths.
+ */
+ if (typlen == -1) /* varlena */
+ {
+ int i;
+ for (i = 0; i < nvalues; i++)
+ {
+ len += VARSIZE_ANY(range->values[i]);
+ }
+ }
+ else if (typlen == -2) /* cstring */
+ {
+ int i;
+ for (i = 0; i < nvalues; i++)
+ {
+ /* don't forget to include the null terminator ;-) */
+ len += strlen(DatumGetPointer(range->values[i])) + 1;
+ }
+ }
+ else /* fixed-length types (even by-reference) */
+ {
+ Assert(typlen > 0);
+ len += nvalues * typlen;
+ }
+
+ /*
+ * Allocate the serialized object, copy the basic information. The
+ * serialized object is a varlena, so update the header.
+ */
+ serialized = (SerializedRanges *) palloc0(len);
+ SET_VARSIZE(serialized, len);
+
+ serialized->typid = typid;
+ serialized->nranges = range->nranges;
+ serialized->nvalues = range->nvalues;
+ serialized->maxvalues = range->maxvalues;
+
+ /*
+ * And now copy also the boundary values (like the length calculation
+ * this depends on the particular data type).
+ */
+ ptr = serialized->data; /* start of the serialized data */
+
+ for (i = 0; i < nvalues; i++)
+ {
+ if (typbyval) /* simple by-value data types */
+ {
+ memcpy(ptr, &range->values[i], typlen);
+ ptr += typlen;
+ }
+ else if (typlen > 0) /* fixed-length by-ref types */
+ {
+ memcpy(ptr, DatumGetPointer(range->values[i]), typlen);
+ ptr += typlen;
+ }
+ else if (typlen == -1) /* varlena */
+ {
+ int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i]));
+ memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
+ ptr += tmp;
+ }
+ else if (typlen == -2) /* cstring */
+ {
+ int tmp = strlen(DatumGetPointer(range->values[i])) + 1;
+ memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
+ ptr += tmp;
+ }
+
+ /* make sure we haven't overflown the buffer end */
+ Assert(ptr <= ((char *)serialized + len));
+ }
+
+ /* exact size */
+ Assert(ptr == ((char *)serialized + len));
+
+ return serialized;
+}
+
+/*
+ * range_deserialize
+ * Serialize the in-memory representation into a compact varlena value.
+ *
+ * Simply copy the header and then also the individual values, as stored
+ * in the in-memory value array.
+ */
+static Ranges *
+range_deserialize(SerializedRanges *serialized)
+{
+ int i,
+ nvalues;
+ char *ptr;
+ bool typbyval;
+ int typlen;
+
+ Ranges *range;
+
+ Assert(serialized->nranges >= 0);
+ Assert(serialized->nvalues >= 0);
+ Assert(serialized->maxvalues > 0);
+
+ nvalues = 2*serialized->nranges + serialized->nvalues;
+
+ Assert(nvalues <= serialized->maxvalues);
+
+ range = minmax_multi_init(serialized->maxvalues);
+
+ /* copy the header info */
+ range->nranges = serialized->nranges;
+ range->nvalues = serialized->nvalues;
+ range->maxvalues = serialized->maxvalues;
+ range->typid = serialized->typid;
+
+ typbyval = get_typbyval(serialized->typid);
+ typlen = get_typlen(serialized->typid);
+
+ /*
+ * And now deconstruct the values into Datum array. We don't need
+ * to copy the values and will instead just point the values to the
+ * serialized varlena value (assuming it will be kept around).
+ */
+ ptr = serialized->data;
+
+ for (i = 0; i < nvalues; i++)
+ {
+ if (typbyval) /* simple by-value data types */
+ {
+ memcpy(&range->values[i], ptr, typlen);
+ ptr += typlen;
+ }
+ else if (typlen > 0) /* fixed-length by-ref types */
+ {
+ /* no copy, just set the value to the pointer */
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += typlen;
+ }
+ else if (typlen == -1) /* varlena */
+ {
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += VARSIZE_ANY(DatumGetPointer(range->values[i]));
+ }
+ else if (typlen == -2) /* cstring */
+ {
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += strlen(DatumGetPointer(range->values[i])) + 1;
+ }
+
+ /* make sure we haven't overflown the buffer end */
+ Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized)));
+ }
+
+ /* should have consumed the whole input value exactly */
+ Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized)));
+
+ /* return the deserialized value */
+ return range;
+}
+
+typedef struct compare_context
+{
+ FmgrInfo *cmpFn;
+ Oid colloid;
+} compare_context;
+
+/*
+ * Used to represent ranges expanded during merging and combining (to
+ * reduce number of boundary values to store).
+ *
+ * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to
+ * something ExpandedRange or so.
+ */
+typedef struct CombineRange
+{
+ Datum minval; /* lower boundary */
+ Datum maxval; /* upper boundary */
+ bool collapsed; /* true if minval==maxval */
+} CombineRange;
+
+/*
+ * compare_combine_ranges
+ * Compare the combine ranges - first by minimum, then by maximum.
+ *
+ * We do guarantee that ranges in a single Range object do not overlap,
+ * so it may seem strange that we don't order just by minimum. But when
+ * merging two Ranges (which happens in the union function), the ranges
+ * may in fact overlap. So we do compare both.
+ */
+static int
+compare_combine_ranges(const void *a, const void *b, void *arg)
+{
+ CombineRange *ra = (CombineRange *)a;
+ CombineRange *rb = (CombineRange *)b;
+ Datum r;
+
+ compare_context *cxt = (compare_context *)arg;
+
+ /* first compare minvals */
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, ra->minval, rb->minval);
+
+ if (DatumGetBool(r))
+ return -1;
+
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, rb->minval, ra->minval);
+
+ if (DatumGetBool(r))
+ return 1;
+
+ /* then compare maxvals */
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, ra->maxval, rb->maxval);
+
+ if (DatumGetBool(r))
+ return -1;
+
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, rb->maxval, ra->maxval);
+
+ if (DatumGetBool(r))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * range_contains_value
+ * See if the new value is already contained in the range list.
+ *
+ * We first inspect the list of intervals. We use a small trick - we check
+ * the value against min/max of the whole range (min of the first interval,
+ * max of the last one) first, and only inspect the individual intervals if
+ * this passes.
+ *
+ * If the value matches none of the intervals, we check the exact values.
+ * We simply loop through them and invoke equality operator on them.
+ *
+ * XXX This might benefit from the fact that both the intervals and exact
+ * values are sorted - we might do bsearch or something. Currently that
+ * does not make much difference (there are only ~32 intervals), but if
+ * this gets increased and/or the comparator function is more expensive,
+ * it might be a huge win.
+ */
+static bool
+range_contains_value(BrinDesc *bdesc, Oid colloid,
+ AttrNumber attno, Form_pg_attribute attr,
+ Ranges *ranges, Datum newval)
+{
+ int i;
+ FmgrInfo *cmpLessFn;
+ FmgrInfo *cmpGreaterFn;
+ FmgrInfo *cmpEqualFn;
+ Oid typid = attr->atttypid;
+
+ /*
+ * First inspect the ranges, if there are any. We first check the whole
+ * range, and only when there's still a chance of getting a match we
+ * inspect the individual ranges.
+ */
+ if (ranges->nranges > 0)
+ {
+ Datum compar;
+ bool match = true;
+
+ Datum minvalue = ranges->values[0];
+ Datum maxvalue = ranges->values[2*ranges->nranges - 1];
+
+ /*
+ * Otherwise, need to compare the new value with boundaries of all
+ * the ranges. First check if it's less than the absolute minimum,
+ * which is the first value in the array.
+ */
+ cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTLessStrategyNumber);
+ compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue);
+
+ /* smaller than the smallest value in the range list */
+ if (DatumGetBool(compar))
+ match = false;
+
+ /*
+ * And now compare it to the existing maximum (last value in the
+ * data array). But only if we haven't already ruled out a possible
+ * match in the minvalue check.
+ */
+ if (match)
+ {
+ cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTGreaterStrategyNumber);
+ compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue);
+
+ if (DatumGetBool(compar))
+ match = false;
+ }
+
+ /*
+ * So it's in the general range, but is it actually covered by any
+ * of the ranges? Repeat the check for each range.
+ *
+ * XXX We simply walk the ranges sequentially, but maybe we could
+ * further leverage the ordering and non-overlap and use bsearch to
+ * speed this up a bit.
+ */
+ for (i = 0; i < ranges->nranges && match; i++)
+ {
+ /* copy the min/max values from the ranges */
+ minvalue = ranges->values[2*i];
+ maxvalue = ranges->values[2*i+1];
+
+ /*
+ * Otherwise, need to compare the new value with boundaries of all
+ * the ranges. First check if it's less than the absolute minimum,
+ * which is the first value in the array.
+ */
+ compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue);
+
+ /* smaller than the smallest value in this range */
+ if (DatumGetBool(compar))
+ continue;
+
+ compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue);
+
+ /* larger than the largest value in this range */
+ if (DatumGetBool(compar))
+ continue;
+
+ /* hey, we found a matching row */
+ return true;
+ }
+ }
+
+ cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTEqualStrategyNumber);
+
+ /*
+ * We're done with the ranges, now let's inspect the exact values.
+ *
+ * XXX Again, we do sequentially search the values - consider leveraging
+ * the ordering of values to improve performance.
+ */
+ for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++)
+ {
+ Datum compar;
+
+ compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]);
+
+ /* found an exact match */
+ if (DatumGetBool(compar))
+ return true;
+ }
+
+ /* the value is not covered by this BRIN tuple */
+ return false;
+}
+
+/*
+ * insert_value
+ * Adds a new value into the single-point part, while maintaining ordering.
+ *
+ * The function inserts the new value to the right place in the single-point
+ * part of the range. It assumes there's enough free space, and then does
+ * essentially an insert-sort.
+ *
+ * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that
+ * only the first nvalues are used.
+ */
+static void
+insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues,
+ Datum newvalue)
+{
+ int i;
+ Datum lt;
+
+ /* If there are no values yet, store the new one and we're done. */
+ if (!nvalues)
+ {
+ values[0] = newvalue;
+ return;
+ }
+
+ /*
+ * A common case is that the new value is entirely out of the existing
+ * range, i.e. it's either smaller or larger than all previous values.
+ * So we check and handle this case first - first we check the larger
+ * case, because in that case we can just append the value to the end
+ * of the array and we're done.
+ */
+
+ /* Is it greater than all existing values in the array? */
+ lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue);
+ if (DatumGetBool(lt))
+ {
+ /* just copy it in-place and we're done */
+ values[nvalues] = newvalue;
+ return;
+ }
+
+ /*
+ * OK, I lied a bit - we won't check the smaller case explicitly, but
+ * we'll just compare the value to all existing values in the array.
+ * But we happen to start with the smallest value, so we're actually
+ * doing the check anyway.
+ *
+ * XXX We do walk the values sequentially. Perhaps we could/should be
+ * smarter and do some sort of bisection, to improve performance?
+ */
+ for (i = 0; i < nvalues; i++)
+ {
+ lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]);
+ if (DatumGetBool(lt))
+ {
+ /*
+ * Move values to make space for the new entry, which should go
+ * to index 'i'. Entries 0 ... (i-1) should stay where they are.
+ */
+ memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum));
+ values[i] = newvalue;
+ return;
+ }
+ }
+
+ /* We should never really get here. */
+ Assert(false);
+}
+
+/*
+ * Check that the order of the array values is correct, using the cmp
+ * function (which should be BTLessStrategyNumber).
+ */
+static void
+AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues)
+{
+#ifdef USE_ASSERT_CHECKING
+ int i;
+ Datum lt;
+
+ for (i = 0; i < (nvalues-1); i++)
+ {
+ lt = FunctionCall2Coll(cmp, colloid, values[i], values[i+1]);
+ Assert(DatumGetBool(lt));
+ }
+#endif
+}
+
+/*
+ * Check ordering of boundary values in both parts of the ranges, using
+ * the cmp function (which should be BTLessStrategyNumber).
+ *
+ * XXX We might also check that the ranges and points do not overlap. But
+ * that will break this check sooner or later anyway.
+ */
+static void
+AssertRangeOrdering(FmgrInfo *cmpFn, Oid colloid, Ranges *ranges)
+{
+#ifdef USE_ASSERT_CHECKING
+ /* first the ranges (there are 2*nranges boundary values) */
+ AssertArrayOrder(cmpFn, colloid, ranges->values, 2*ranges->nranges);
+
+ /* then the single-point ranges (with nvalues boundar values ) */
+ AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges],
+ ranges->nvalues);
+#endif
+}
+
+/*
+ * Check that the expanded ranges (built when reducing the number of ranges
+ * by combining some of them) are correctly sorted and do not overlap.
+ */
+static void
+AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno,
+ Form_pg_attribute attr, CombineRange *ranges,
+ int nranges)
+{
+#ifdef USE_ASSERT_CHECKING
+ int i;
+ FmgrInfo *eq;
+ FmgrInfo *lt;
+
+ eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTEqualStrategyNumber);
+
+ lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /*
+ * Each range independently should be valid, i.e. that for the boundary
+ * values (lower <= upper).
+ */
+ for (i = 0; i < nranges; i++)
+ {
+ Datum r;
+ Datum minval = ranges[i].minval;
+ Datum maxval = ranges[i].maxval;
+
+ if (ranges[i].collapsed) /* collapsed: minval == maxval */
+ r = FunctionCall2Coll(eq, colloid, minval, maxval);
+ else /* non-collapsed: minval < maxval */
+ r = FunctionCall2Coll(lt, colloid, minval, maxval);
+
+ Assert(DatumGetBool(r));
+ }
+
+ /*
+ * And the ranges should be ordered and must nor overlap, i.e.
+ * upper < lower for boundaries of consecutive ranges.
+ */
+ for (i = 0; i < nranges-1; i++)
+ {
+ Datum r;
+ Datum maxval = ranges[i].maxval;
+ Datum minval = ranges[i+1].minval;
+
+ r = FunctionCall2Coll(lt, colloid, maxval, minval);
+
+ Assert(DatumGetBool(r));
+ }
+#endif
+}
+
+/*
+ * Expand ranges from Ranges into CombineRange array. This expects the
+ * cranges to be pre-allocated and sufficiently large (there needs to be
+ * at least (nranges + nvalues) slots).
+ */
+static void
+fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
+{
+ int idx;
+ int i;
+
+ idx = 0;
+ for (i = 0; i < ranges->nranges; i++)
+ {
+ cranges[idx].minval = ranges->values[2*i];
+ cranges[idx].maxval = ranges->values[2*i+1];
+ cranges[idx].collapsed = false;
+ idx++;
+
+ Assert(idx <= ncranges);
+ }
+
+ for (i = 0; i < ranges->nvalues; i++)
+ {
+ cranges[idx].minval = ranges->values[2*ranges->nranges + i];
+ cranges[idx].maxval = ranges->values[2*ranges->nranges + i];
+ cranges[idx].collapsed = true;
+ idx++;
+
+ Assert(idx <= ncranges);
+ }
+
+ Assert(idx == ncranges);
+
+ return;
+}
+
+/*
+ * Sort combine ranges using qsort (with BTLessStrategyNumber function).
+ */
+static void
+sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
+ CombineRange *cranges, int ncranges)
+{
+ compare_context cxt;
+
+ /* sort the values */
+ cxt.colloid = colloid;
+ cxt.cmpFn = cmp;
+
+ qsort_arg(cranges, ncranges, sizeof(CombineRange),
+ compare_combine_ranges, (void *) &cxt);
+}
+
+/*
+ * When combining multiple Range values (in union function), some of the
+ * ranges may overlap. We simply merge the overlapping ranges to fix that.
+ *
+ * XXX This assumes the combine ranges were previously sorted (by minval
+ * and then maxval). We leverage this when detecting overlap.
+ */
+static int
+merge_combine_ranges(FmgrInfo *cmp, Oid colloid,
+ CombineRange *cranges, int ncranges)
+{
+ int idx;
+
+ /* TODO: add assert checking the ordering of input ranges */
+
+ /* try merging ranges (idx) and (idx+1) if they overlap */
+ idx = 0;
+ while (idx < (ncranges-1))
+ {
+ Datum r;
+
+ /*
+ * comparing [?,maxval] vs. [minval,?] - the ranges overlap
+ * if (minval < maxval)
+ */
+ r = FunctionCall2Coll(cmp, colloid,
+ cranges[idx].maxval,
+ cranges[idx+1].minval);
+
+ /*
+ * Nope, maxval < minval, so no overlap. And we know the ranges
+ * are ordered, so there are no more overlaps, because all the
+ * remaining ranges have greater or equal minval.
+ */
+ if (DatumGetBool(r))
+ {
+ /* proceed to the next range */
+ idx += 1;
+ continue;
+ }
+
+ /*
+ * So ranges 'idx' and 'idx+1' do overlap, but we don't know if
+ * 'idx+1' is contained in 'idx', or if they only overlap only
+ * partially. So compare the upper bounds and keep the larger one.
+ */
+ r = FunctionCall2Coll(cmp, colloid,
+ cranges[idx].maxval,
+ cranges[idx+1].maxval);
+
+ if (DatumGetBool(r))
+ cranges[idx].maxval = cranges[idx+1].maxval;
+
+ /*
+ * The range certainly is no longer collapsed (irrespectedly of
+ * the previous state).
+ */
+ cranges[idx].collapsed = false;
+
+ /*
+ * Now get rid of the (idx+1) range entirely by shifting the
+ * remaining ranges by 1. There are ncranges elements, and we
+ * need to move elements from (idx+2). That means the number
+ * of elements to move is [ncranges - (idx+2)].
+ */
+ memmove(&cranges[idx+1], &cranges[idx+2],
+ (ncranges - (idx + 2)) * sizeof(CombineRange));
+
+ /*
+ * Decrease the number of ranges, and repeat (with the same range,
+ * as it might overlap with additional ranges thanks to the merge).
+ */
+ ncranges--;
+ }
+
+ /* TODO: add assert checking the ordering etc. of produced ranges */
+
+ return ncranges;
+}
+
+/*
+ * Represents a distance between two ranges (identified by index into
+ * an array of combine ranges).
+ */
+typedef struct DistanceValue
+{
+ int index;
+ double value;
+} DistanceValue;
+
+/*
+ * Simple comparator for distance values, comparing the double value.
+ */
+static int
+compare_distances(const void *a, const void *b)
+{
+ DistanceValue *da = (DistanceValue *)a;
+ DistanceValue *db = (DistanceValue *)b;
+
+ if (da->value < db->value)
+ return -1;
+ else if (da->value > db->value)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Given an array of combine ranges, compute distance of the gaps betwen
+ * the ranges - for ncranges there are (ncranges-1) gaps.
+ *
+ * We simply call the "distance" function to compute the (min-max) for pairs
+ * of consecutive ganges. The function may be fairly expensive, so we do that
+ * just once (and then use it to pick as many ranges to merge as possible).
+ *
+ * See reduce_combine_ranges for details.
+ */
+static DistanceValue *
+build_distances(FmgrInfo *distanceFn, Oid colloid,
+ CombineRange *cranges, int ncranges)
+{
+ int i;
+ DistanceValue *distances;
+
+ Assert(ncranges >= 2);
+
+ distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1));
+
+ /*
+ * Walk though the ranges once and compute distance between the ranges
+ * so that we can sort them once.
+ */
+ for (i = 0; i < (ncranges-1); i++)
+ {
+ Datum a1, a2, r;
+
+ a1 = cranges[i].maxval;
+ a2 = cranges[i+1].minval;
+
+ /* compute length of the empty gap (distance between max/min) */
+ r = FunctionCall2Coll(distanceFn, colloid, a1, a2);
+
+ /* remember the index */
+ distances[i].index = i;
+ distances[i].value = DatumGetFloat8(r);
+ }
+
+ /* sort the distances in ascending order */
+ pg_qsort(distances, (ncranges-1), sizeof(DistanceValue), compare_distances);
+
+ return distances;
+}
+
+/*
+ * Builds combine ranges for the existing ranges (and single-point ranges),
+ * and also the new value (which did not fit into the array).
+ *
+ * XXX We do perform qsort on all the values, but we could also leverage
+ * the fact that the input data is already sorted and do merge sort.
+ */
+static CombineRange *
+build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
+ Datum newvalue, int *nranges)
+{
+ int ncranges;
+ CombineRange *cranges;
+
+ /* now do the actual merge sort */
+ ncranges = ranges->nranges + ranges->nvalues + 1;
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
+ *nranges = ncranges;
+
+ /* put the new value at the beginning */
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
+
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+
+ /* and sort the ranges */
+ sort_combine_ranges(cmp, colloid, cranges, ncranges);
+
+ return cranges;
+}
+
+/*
+ * Counts bondary values needed to store the ranges. Each single-point
+ * range is stored using a single value, each regular range needs two.
+ */
+static int
+count_values(CombineRange *cranges, int ncranges)
+{
+ int i;
+ int count;
+
+ count = 0;
+ for (i = 0; i < ncranges; i++)
+ {
+ if (cranges[i].collapsed)
+ count += 1;
+ else
+ count += 2;
+ }
+
+ return count;
+}
+
+/*
+ * Combines ranges until the number of boundary values drops below 75%
+ * of the capacity (as set by values_per_range reloption).
+ *
+ * XXX The ranges to merge are selected solely using the distance. But
+ * that may not be the best strategy, for example when multiple gaps
+ * are of equal (or very similar) length.
+ *
+ * Consider for example points 1, 2, 3, .., 64, which have gaps of the
+ * same length 1 of course. In that case we tend to pick the first
+ * gap of that length, which leads to this:
+ *
+ * step 1: [1, 2], 3, 4, 5, .., 64
+ * step 2: [1, 3], 4, 5, .., 64
+ * step 3: [1, 4], 5, .., 64
+ * ...
+ *
+ * So in the end we'll have one "large" range and multiple small points.
+ * That may be fine, but it seems a bit strange and non-optimal. Maybe
+ * we should consider other things when picking ranges to merge - e.g.
+ * length of the ranges? Or perhaps randomize the choice of ranges, with
+ * probability inversely proportional to the distance (the gap lengths
+ * may be very close, but not exactly the same).
+ */
+static int
+reduce_combine_ranges(CombineRange *cranges, int ncranges,
+ DistanceValue *distances, int max_values)
+{
+ int i;
+ int ndistances = (ncranges - 1);
+ int count = count_values(cranges, ncranges);
+
+ /*
+ * We have one fewer 'gaps' than the ranges. We'll be decrementing
+ * the number of combine ranges (reduction is the primary goal of
+ * this function), so we must use a separate value.
+ */
+ for (i = 0; i < ndistances; i++)
+ {
+ int j;
+ int shortest;
+
+ if (count <= max_values * 0.75)
+ break;
+
+ shortest = distances[i].index;
+
+ /*
+ * The index must be still valid with respect to the current size
+ * of cranges array (and it always points to the first range, so
+ * never to the last one - hence the -1 in the condition).
+ */
+ Assert(shortest < (ncranges - 1));
+
+ if (!cranges[shortest].collapsed && !cranges[shortest+1].collapsed)
+ count -= 2;
+ else if (!cranges[shortest].collapsed || !cranges[shortest+1].collapsed)
+ count -= 1;
+
+ /*
+ * Move the values to join the two selected ranges. The new range is
+ * definiely not collapsed but a regular range.
+ */
+ cranges[shortest].maxval = cranges[shortest+1].maxval;
+ cranges[shortest].collapsed = false;
+
+ /* shuffle the subsequent combine ranges */
+ memmove(&cranges[shortest+1], &cranges[shortest+2],
+ (ncranges - shortest - 2) * sizeof(CombineRange));
+
+ /* also, shuffle all higher indexes (we've just moved the ranges) */
+ for (j = i; j < ndistances; j++)
+ {
+ if (distances[j].index > shortest)
+ distances[j].index--;
+ }
+
+ ncranges--;
+
+ Assert(ncranges > 0);
+ }
+
+ return ncranges;
+}
+
+/*
+ * Store the boundary values from CombineRanges back into Range (using
+ * only the minimal number of values needed).
+ */
+static void
+store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges)
+{
+ int i;
+ int idx = 0;
+
+ /* first copy in the regular ranges */
+ ranges->nranges = 0;
+ for (i = 0; i < ncranges; i++)
+ {
+ if (!cranges[i].collapsed)
+ {
+ ranges->values[idx++] = cranges[i].minval;
+ ranges->values[idx++] = cranges[i].maxval;
+ ranges->nranges++;
+ }
+ }
+
+ /* now copy in the collapsed ones */
+ ranges->nvalues = 0;
+ for (i = 0; i < ncranges; i++)
+ {
+ if (cranges[i].collapsed)
+ {
+ ranges->values[idx++] = cranges[i].minval;
+ ranges->nvalues++;
+ }
+ }
+}
+
+/*
+ * range_add_value
+ * Add the new value to the multi-minmax range.
+ */
+static bool
+range_add_value(BrinDesc *bdesc, Oid colloid,
+ AttrNumber attno, Form_pg_attribute attr,
+ Ranges *ranges, Datum newval)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+
+ /*
+ * Bail out if the value already is covered by the range.
+ *
+ * We could also add values until we hit values_per_range, and then
+ * do the deduplication in a batch, hoping for better efficiency. But
+ * that would mean we actually modify the range every time, which means
+ * having to serialize the value, which does palloc, walks the values,
+ * copies them, etc. Not exactly cheap.
+ *
+ * So instead we do the check, which should be fairly cheap - assuming
+ * the comparator function is not very expensive.
+ *
+ * This also implies means the values array can't contain duplicities.
+ */
+ if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval))
+ return false;
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /*
+ * If there's space in the values array, copy it in and we're done.
+ *
+ * We do want to keep the values sorted (to speed up searches), so we
+ * do a simple insertion sort. We could do something more elaborate,
+ * e.g. by sorting the values only now and then, but for small counts
+ * (e.g. when maxvalues is 64) this should be fine.
+ */
+ if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues)
+ {
+ Datum *values;
+
+ /* beginning of the 'single value' part (for convenience) */
+ values = &ranges->values[2*ranges->nranges];
+
+ insert_value(cmpFn, colloid, values, ranges->nvalues, newval);
+
+ ranges->nvalues++;
+
+ /*
+ * Check we haven't broken the ordering of boundary values (checks
+ * both parts, but that doesn't hurt).
+ */
+ AssertRangeOrdering(cmpFn, colloid, ranges);
+
+ /* yep, we've modified the range */
+ return true;
+ }
+
+ /*
+ * Damn - the new value is not in the range yet, but we don't have space
+ * to just insert it. So we need to combine some of the existing ranges,
+ * to reduce the number of values we need to store (joining two intervals
+ * reduces the number of boundaries to store by 2).
+ *
+ * To do that we first construct an array of CombineRange items - each
+ * combine range tracks if it's a regular range or collapsed range, where
+ * "collapsed" means "single point."
+ *
+ * Existing ranges (we have ranges->nranges of them) map to combine ranges
+ * directly, while single points (ranges->nvalues of them) have to be
+ * expanded. We neet the combine ranges to be sorted, and we do that by
+ * performing a merge sort of ranges, values and new value.
+ */
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we release at least 25% of the space. This
+ * threshold is somewhat arbitrary, perhaps needs tuning. We must not
+ * use too low or high value.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ ranges->maxvalues);
+
+ Assert(count_values(cranges, ncranges) <= ranges->maxvalues * 0.75);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+
+ return true;
+}
+
+Datum
+brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
+{
+ BrinOpcInfo *result;
+
+ /*
+ * opaque->strategy_procinfos is initialized lazily; here it is set to
+ * all-uninitialized by palloc0 which sets fn_oid to InvalidOid.
+ */
+
+ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) +
+ sizeof(MinmaxMultiOpaque));
+ result->oi_nstored = 1;
+ result->oi_regular_nulls = true;
+ result->oi_opaque = (MinmaxMultiOpaque *)
+ MAXALIGN((char *) result + SizeofBrinOpcInfo(1));
+ result->oi_typcache[0] = lookup_type_cache(BRINMINMAXMULTISUMMARYOID, 0);
+
+ PG_RETURN_POINTER(result);
+}
+
+/*
+ * Compute distance between two float4 values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS)
+{
+ float a1 = PG_GETARG_FLOAT4(0);
+ float a2 = PG_GETARG_FLOAT4(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(a1 <= a2);
+
+ PG_RETURN_FLOAT8((double) a2 - (double) a1);
+}
+
+/*
+ * Compute distance between two float8 values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS)
+{
+ double a1 = PG_GETARG_FLOAT8(0);
+ double a2 = PG_GETARG_FLOAT8(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(a1 <= a2);
+
+ PG_RETURN_FLOAT8(a2 - a1);
+}
+
+/*
+ * Compute distance between two int2 values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS)
+{
+ int16 a1 = PG_GETARG_INT16(0);
+ int16 a2 = PG_GETARG_INT16(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(a1 <= a2);
+
+ PG_RETURN_FLOAT8((double) a2 - (double) a1);
+}
+
+/*
+ * Compute distance between two int4 values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS)
+{
+ int32 a1 = PG_GETARG_INT32(0);
+ int32 a2 = PG_GETARG_INT32(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(a1 <= a2);
+
+ PG_RETURN_FLOAT8((double) a2 - (double) a1);
+}
+
+/*
+ * Compute distance between two int8 values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS)
+{
+ int64 a1 = PG_GETARG_INT64(0);
+ int64 a2 = PG_GETARG_INT64(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(a1 <= a2);
+
+ PG_RETURN_FLOAT8((double) a2 - (double) a1);
+}
+
+/*
+ * Compute distance between two tid values (by mapping them to float8
+ * and then subtracting them).
+ */
+Datum
+brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS)
+{
+ double da1,
+ da2;
+
+ ItemPointer pa1 = (ItemPointer) PG_GETARG_DATUM(0);
+ ItemPointer pa2 = (ItemPointer) PG_GETARG_DATUM(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(ItemPointerCompare(pa1, pa2) <= 0);
+
+ da1 = ItemPointerGetBlockNumber(pa1) * MaxHeapTuplesPerPage +
+ ItemPointerGetOffsetNumber(pa1);
+
+ da2 = ItemPointerGetBlockNumber(pa2) * MaxHeapTuplesPerPage +
+ ItemPointerGetOffsetNumber(pa2);
+
+ PG_RETURN_FLOAT8(da2 - da1);
+}
+
+/*
+ * Comutes distance between two numeric values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS)
+{
+ Datum d;
+ Datum a1 = PG_GETARG_DATUM(0);
+ Datum a2 = PG_GETARG_DATUM(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2)));
+
+ d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */
+
+ PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d));
+}
+
+/*
+ * Computes approximate distance between two UUID values.
+ *
+ * XXX We do not need a perfectly accurate value, so we approximate the
+ * deltas (which would have to be 128-bit integers) with a 64-bit float.
+ * The small inaccuracies do not matter in practice, in the worst case
+ * we'll decide to merge ranges that are not the closest ones.
+ */
+Datum
+brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS)
+{
+ int i;
+ double delta = 0;
+
+ Datum a1 = PG_GETARG_DATUM(0);
+ Datum a2 = PG_GETARG_DATUM(1);
+
+ pg_uuid_t *u1 = DatumGetUUIDP(a1);
+ pg_uuid_t *u2 = DatumGetUUIDP(a2);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(DatumGetBool(DirectFunctionCall2(uuid_le, a1, a2)));
+
+ /* compute approximate delta as a double precision value */
+ for (i = UUID_LEN-1; i >= 0; i--)
+ {
+ delta += (int) u2->data[i] - (int) u1->data[i];
+ delta /= 256;
+ }
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Computes approximate distance between two time (without tz) values.
+ *
+ * TimeADT is just an int64, so we simply subtract the values directly.
+ */
+Datum
+brin_minmax_multi_distance_time(PG_FUNCTION_ARGS)
+{
+ double delta = 0;
+
+ TimeADT ta = PG_GETARG_TIMEADT(0);
+ TimeADT tb = PG_GETARG_TIMEADT(1);
+
+ delta = (tb - ta);
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Computes approximate distance between two timetz values.
+ *
+ * Simply subtracts the TimeADT (int64) values embedded in TimeTzADT.
+ *
+ * XXX Does this need to consider the time zones?
+ */
+Datum
+brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS)
+{
+ double delta = 0;
+
+ TimeTzADT *ta = PG_GETARG_TIMETZADT_P(0);
+ TimeTzADT *tb = PG_GETARG_TIMETZADT_P(1);
+
+ delta = tb->time - ta->time;
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Computes distance between two interval values.
+ *
+ * Intervals are internally just 'time' values, so use the same approach
+ * as for in brin_minmax_multi_distance_time.
+ *
+ * XXX Do we actually need two separate functions, then?
+ */
+Datum
+brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS)
+{
+ double delta = 0;
+
+ TimeADT ia = PG_GETARG_TIMEADT(0);
+ TimeADT ib = PG_GETARG_TIMEADT(1);
+
+ delta = (ib - ia);
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Compute distance between two pg_lsn values.
+ *
+ * LSN is just an int64 encoding position in the stream, so just subtract
+ * those int64 values directly.
+ */
+Datum
+brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS)
+{
+ double delta = 0;
+
+ XLogRecPtr lsna = PG_GETARG_LSN(0);
+ XLogRecPtr lsnb = PG_GETARG_LSN(1);
+
+ delta = (lsnb - lsna);
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Compute distance between two macaddr values.
+ *
+ * mac addresses are treated as 6 unsigned chars, so do the same thing we
+ * already do for UUID values.
+ */
+Datum
+brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS)
+{
+ double delta;
+
+ macaddr *a = PG_GETARG_MACADDR_P(0);
+ macaddr *b = PG_GETARG_MACADDR_P(1);
+
+ delta = ((double)b->f - (double)a->f);
+ delta /= 256;
+
+ delta += ((double)b->e - (double)a->e);
+ delta /= 256;
+
+ delta += ((double)b->d - (double)a->d);
+ delta /= 256;
+
+ delta += ((double)b->c - (double)a->c);
+ delta /= 256;
+
+ delta += ((double)b->b - (double)a->b);
+ delta /= 256;
+
+ delta += ((double)b->a - (double)a->a);
+ delta /= 256;
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Compute distance between two macaddr8 values.
+ *
+ * macaddr8 addresses are 8 unsigned chars, so do the same thing we
+ * already do for UUID values.
+ */
+Datum
+brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS)
+{
+ double delta;
+
+ macaddr8 *a = PG_GETARG_MACADDR8_P(0);
+ macaddr8 *b = PG_GETARG_MACADDR8_P(1);
+
+ delta = ((double)b->h - (double)a->h);
+ delta /= 256;
+
+ delta += ((double)b->g - (double)a->g);
+ delta /= 256;
+
+ delta += ((double)b->f - (double)a->f);
+ delta /= 256;
+
+ delta += ((double)b->e - (double)a->e);
+ delta /= 256;
+
+ delta += ((double)b->d - (double)a->d);
+ delta /= 256;
+
+ delta += ((double)b->c - (double)a->c);
+ delta /= 256;
+
+ delta += ((double)b->b - (double)a->b);
+ delta /= 256;
+
+ delta += ((double)b->a - (double)a->a);
+ delta /= 256;
+
+ Assert(delta >= 0);
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Compute distance between two inet values.
+ *
+ * The distance is defined as difference between 32-bit/128-bit values,
+ * depending on the IP version. The distance is computed by subtracting
+ * the bytes and normalizing it to [0,1] range for each IP family.
+ * Addresses from difference families are consider to be in maximum
+ * distance, which is 1.0.
+ *
+ * XXX Does this need to consider the mask (bits)? For now it's ignored.
+ */
+Datum
+brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
+{
+ double delta;
+ int i;
+ int len;
+ unsigned char *addra,
+ *addrb;
+
+ inet *ipa = PG_GETARG_INET_PP(0);
+ inet *ipb = PG_GETARG_INET_PP(1);
+
+ /*
+ * If the addresses are from different families, consider them to be
+ * in maximal possible distance (which is 1.0).
+ */
+ if (ip_family(ipa) != ip_family(ipb))
+ return 1.0;
+
+ /* ipv4 or ipv6 */
+ if (ip_family(ipa) == PGSQL_AF_INET)
+ len = 4;
+ else
+ len = 16; /* NS_IN6ADDRSZ */
+
+ addra = ip_addr(ipa);
+ addrb = ip_addr(ipb);
+
+ delta = 0;
+ for (i = len-1; i >= 0; i--)
+ {
+ delta += (double)addrb[i] - (double)addra[i];
+ delta /= 256;
+ }
+
+ Assert((delta >= 0) && (delta <= 1));
+
+ PG_RETURN_FLOAT8(delta);
+}
+
+static void
+brin_minmax_multi_serialize(Datum src, Datum *dst)
+{
+ Ranges *ranges = (Ranges *) DatumGetPointer(src);
+ SerializedRanges *s = range_serialize(ranges);
+ dst[0] = PointerGetDatum(s);
+}
+
+static int
+brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts)
+{
+ return MinMaxGetValuesPerRange(opts);
+}
+
+/*
+ * Examine the given index tuple (which contains partial status of a certain
+ * page range) by comparing it to the given value that comes from another heap
+ * tuple. If the new value is outside the min/max range specified by the
+ * existing tuple values, update the index tuple and return true. Otherwise,
+ * return false and do not modify in this case.
+ */
+Datum
+brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
+{
+ BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+ BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
+ Datum newval = PG_GETARG_DATUM(2);
+ bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3);
+ MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION();
+ bool modified = false;
+ Form_pg_attribute attr;
+ AttrNumber attno;
+ Ranges *ranges;
+ SerializedRanges *serialized = NULL;
+
+ Assert(!isnull);
+
+ attno = column->bv_attno;
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+
+ /* use the already deserialized value, is possible */
+ ranges = (Ranges *) DatumGetPointer(column->bv_mem_value);
+
+ /*
+ * If this is the first non-null value, we need to initialize the range
+ * list. Otherwise just extract the existing range list from BrinValues.
+ */
+ if (column->bv_allnulls)
+ {
+ MemoryContext oldctx;
+
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+
+ ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ ranges->typid = attr->atttypid;
+
+ MemoryContextSwitchTo(oldctx);
+
+ column->bv_allnulls = false;
+ modified = true;
+
+ column->bv_mem_value = PointerGetDatum(ranges);
+ column->bv_serialize = brin_minmax_multi_serialize;
+ }
+ else if (!ranges)
+ {
+ MemoryContext oldctx;
+
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+
+ serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+ ranges = range_deserialize(serialized);
+
+ column->bv_mem_value = PointerGetDatum(ranges);
+ column->bv_serialize = brin_minmax_multi_serialize;
+
+ MemoryContextSwitchTo(oldctx);
+ }
+
+ /*
+ * Try to add the new value to the range. We need to update the modified
+ * flag, so that we serialize the correct value.
+ */
+ modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval);
+
+
+ PG_RETURN_BOOL(modified);
+}
+
+/*
+ * 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.
+ */
+Datum
+brin_minmax_multi_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);
+ // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION(),
+ subtype;
+ AttrNumber attno;
+ Datum value;
+ FmgrInfo *finfo;
+ SerializedRanges *serialized;
+ Ranges *ranges;
+ int keyno;
+ int rangeno;
+ int i;
+
+ attno = column->bv_attno;
+
+ serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+ ranges = range_deserialize(serialized);
+
+ /* inspect the ranges, and for each one evaluate the scan keys */
+ for (rangeno = 0; rangeno < ranges->nranges; rangeno++)
+ {
+ Datum minval = ranges->values[2*rangeno];
+ Datum maxval = ranges->values[2*rangeno+1];
+
+ /* assume the range is matching, and we'll try to prove otherwise */
+ bool matching = true;
+
+ for (keyno = 0; keyno < nkeys; keyno++)
+ {
+ Datum matches;
+ ScanKey key = keys[keyno];
+
+ /* NULL keys are handled and filtered-out in bringetbitmap */
+ Assert(!(key->sk_flags & SK_ISNULL));
+
+ attno = key->sk_attno;
+ subtype = key->sk_subtype;
+ value = key->sk_argument;
+ switch (key->sk_strategy)
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ key->sk_strategy);
+ /* first value from the array */
+ matches = FunctionCall2Coll(finfo, colloid, minval, value);
+ break;
+
+ case BTEqualStrategyNumber:
+ {
+ Datum compar;
+ FmgrInfo *cmpFn;
+
+ /* by default this range does not match */
+ matches = false;
+
+ /*
+ * Otherwise, need to compare the new value with boundaries of all
+ * the ranges. First check if it's less than the absolute minimum,
+ * which is the first value in the array.
+ */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ BTLessStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, colloid, value, minval);
+
+ /* smaller than the smallest value in this range */
+ if (DatumGetBool(compar))
+ break;
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ BTGreaterStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, colloid, value, maxval);
+
+ /* larger than the largest value in this range */
+ if (DatumGetBool(compar))
+ break;
+
+ /* haven't managed to eliminate this range, so consider it matching */
+ matches = true;
+
+ break;
+ }
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ key->sk_strategy);
+ /* last value from the array */
+ matches = FunctionCall2Coll(finfo, colloid, maxval, value);
+ break;
+
+ default:
+ /* shouldn't happen */
+ elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+ matches = 0;
+ break;
+ }
+
+ /* the range has to match all the scan keys */
+ matching &= DatumGetBool(matches);
+
+ /* once we find a non-matching key, we're done */
+ if (! matching)
+ break;
+ }
+
+ /* have we found a range matching all scan keys? if yes, we're
+ * done */
+ if (matching)
+ PG_RETURN_DATUM(BoolGetDatum(true));
+ }
+
+ /* and now inspect the values */
+ for (i = 0; i < ranges->nvalues; i++)
+ {
+ Datum val = ranges->values[2*ranges->nranges + i];
+
+ /* assume the range is matching, and we'll try to prove otherwise */
+ bool matching = true;
+
+ for (keyno = 0; keyno < nkeys; keyno++)
+ {
+ Datum matches;
+ ScanKey key = keys[keyno];
+
+ /* we've already dealt with NULL keys at the beginning */
+ if (key->sk_flags & SK_ISNULL)
+ continue;
+
+ attno = key->sk_attno;
+ subtype = key->sk_subtype;
+ value = key->sk_argument;
+ switch (key->sk_strategy)
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ case BTEqualStrategyNumber:
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+
+ finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ key->sk_strategy);
+ matches = FunctionCall2Coll(finfo, colloid, val, value);
+ break;
+
+ default:
+ /* shouldn't happen */
+ elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+ matches = 0;
+ break;
+ }
+
+ /* the range has to match all the scan keys */
+ matching &= DatumGetBool(matches);
+
+ /* once we find a non-matching key, we're done */
+ if (! matching)
+ break;
+ }
+
+ /* have we found a range matching all scan keys? if yes, we're
+ * done */
+ if (matching)
+ PG_RETURN_DATUM(BoolGetDatum(true));
+ }
+
+ PG_RETURN_DATUM(BoolGetDatum(false));
+}
+
+/*
+ * Given two BrinValues, update the first of them as a union of the summary
+ * values contained in both. The second one is untouched.
+ */
+Datum
+brin_minmax_multi_union(PG_FUNCTION_ARGS)
+{
+ BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+ BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
+ BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
+ // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION();
+ SerializedRanges *serialized_a;
+ SerializedRanges *serialized_b;
+ Ranges *ranges_a;
+ Ranges *ranges_b;
+ AttrNumber attno;
+ Form_pg_attribute attr;
+ CombineRange *cranges;
+ int ncranges;
+ FmgrInfo *cmpFn,
+ *distanceFn;
+ DistanceValue *distances;
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ Assert(col_a->bv_attno == col_b->bv_attno);
+ Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
+
+ attno = col_a->bv_attno;
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+
+ serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]);
+ serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]);
+
+ ranges_a = range_deserialize(serialized_a);
+ ranges_b = range_deserialize(serialized_b);
+
+ /* make sure neither of the ranges is NULL */
+ Assert(ranges_a && ranges_b);
+
+ ncranges = (ranges_a->nranges + ranges_a->nvalues) +
+ (ranges_b->nranges + ranges_b->nvalues);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* allocate and fill */
+ cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+
+ /* fill the combine ranges with entries for the first range */
+ fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
+ ranges_a);
+
+ /* and now add combine ranges for the second range */
+ fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues],
+ ranges_b->nranges + ranges_b->nvalues,
+ ranges_b);
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /* sort the combine ranges */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+
+ /*
+ * We've merged two different lists of ranges, so some of them may be
+ * overlapping. So walk through them and merge them.
+ */
+ ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges);
+
+ /* check that the combine ranges are correct (no overlaps, ordering) */
+ AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges);
+
+ /* build array of gap distances and sort them in ascending order */
+ distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE);
+ distances = build_distances(distanceFn, colloid, cranges, ncranges);
+
+ /*
+ * See how many values would be needed to store the current ranges, and if
+ * needed combine as many off them to get below the maxvalues threshold.
+ * The collapsed ranges will be stored as a single value.
+ *
+ * XXX The maxvalues may be different, so perhaps use Max?
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ ranges_a->maxvalues);
+
+ /* update the first range summary */
+ store_combine_ranges(ranges_a, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* cleanup and update the serialized value */
+ pfree(serialized_a);
+ col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a));
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Cache and return minmax multi opclass support procedure
+ *
+ * Return the procedure corresponding to the given function support number
+ * or null if it is not exists.
+ */
+static FmgrInfo *
+minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
+{
+ MinmaxMultiOpaque *opaque;
+ uint16 basenum = procnum - PROCNUM_BASE;
+
+ /*
+ * We cache these in the opaque struct, to avoid repetitive syscache
+ * lookups.
+ */
+ opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+ /*
+ * If we already searched for this proc and didn't find it, don't bother
+ * searching again.
+ */
+ if (opaque->extra_proc_missing[basenum])
+ return NULL;
+
+ if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
+ {
+ if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
+ procnum)))
+ {
+ fmgr_info_copy(&opaque->extra_procinfos[basenum],
+ index_getprocinfo(bdesc->bd_index, attno, procnum),
+ bdesc->bd_context);
+ }
+ else
+ {
+ opaque->extra_proc_missing[basenum] = true;
+ return NULL;
+ }
+ }
+
+ return &opaque->extra_procinfos[basenum];
+}
+
+/*
+ * Cache and return the procedure for the given strategy.
+ *
+ * Note: this function mirrors minmax_multi_get_strategy_procinfo; see notes
+ * there. If changes are made here, see that function too.
+ */
+static FmgrInfo *
+minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
+ uint16 strategynum)
+{
+ MinmaxMultiOpaque *opaque;
+
+ Assert(strategynum >= 1 &&
+ strategynum <= BTMaxStrategyNumber);
+
+ opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+ /*
+ * We cache the procedures for the previous subtype in the opaque struct,
+ * to avoid repetitive syscache lookups. If the subtype changed,
+ * invalidate all the cached entries.
+ */
+ if (opaque->cached_subtype != subtype)
+ {
+ uint16 i;
+
+ for (i = 1; i <= BTMaxStrategyNumber; i++)
+ opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
+ opaque->cached_subtype = subtype;
+ }
+
+ if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
+ {
+ Form_pg_attribute attr;
+ HeapTuple tuple;
+ Oid opfamily,
+ oprid;
+ bool isNull;
+
+ opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+ tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(attr->atttypid),
+ ObjectIdGetDatum(subtype),
+ Int16GetDatum(strategynum));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
+ strategynum, attr->atttypid, subtype, opfamily);
+
+ oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple,
+ Anum_pg_amop_amopopr, &isNull));
+ ReleaseSysCache(tuple);
+ Assert(!isNull && RegProcedureIsValid(oprid));
+
+ fmgr_info_cxt(get_opcode(oprid),
+ &opaque->strategy_procinfos[strategynum - 1],
+ bdesc->bd_context);
+ }
+
+ return &opaque->strategy_procinfos[strategynum - 1];
+}
+
+Datum
+brin_minmax_multi_options(PG_FUNCTION_ARGS)
+{
+ local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+ init_local_reloptions(relopts, sizeof(MinMaxOptions));
+
+ add_local_int_reloption(relopts, "values_per_range", "desc",
+ 32, 16, 256, offsetof(MinMaxOptions, valuesPerRange));
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * brin_minmax_multi_summary_in
+ * - input routine for type brin_minmax_multi_summary.
+ *
+ * brin_minmax_multi_summary is only used internally to represent summaries
+ * in BRIN minmax-multi indexes, so it has no operations of its own, and we
+ * disallow input too.
+ */
+Datum
+brin_minmax_multi_summary_in(PG_FUNCTION_ARGS)
+{
+ /*
+ * brin_minmax_multi_summary stores the data in binary form and parsing
+ * text input is not needed, so disallow this.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "brin_minmax_multi_summary")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+
+/*
+ * brin_minmax_multi_summary_out
+ * - output routine for type brin_minmax_multi_summary.
+ *
+ * BRIN minmax-multi summaries are serialized into a bytea value, but we
+ * want to output something nicer humans can understand.
+ */
+Datum
+brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
+{
+ int i;
+ int idx;
+ SerializedRanges *ranges;
+ Ranges *ranges_deserialized;
+ StringInfoData str;
+ bool isvarlena;
+ Oid outfunc;
+ FmgrInfo fmgrinfo;
+ ArrayBuildState *astate_values = NULL;
+
+ initStringInfo(&str);
+ appendStringInfoChar(&str, '{');
+
+ /*
+ * XXX not sure the detoasting is necessary (probably not, this
+ * can only be in an index).
+ */
+ ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+
+ /* lookup output func for the type */
+ getTypeOutputInfo(ranges->typid, &outfunc, &isvarlena);
+ fmgr_info(outfunc, &fmgrinfo);
+
+ /* deserialize the range info easy-to-process pieces */
+ ranges_deserialized = range_deserialize(ranges);
+
+ appendStringInfo(&str, "nranges: %u nvalues: %u maxvalues: %u",
+ ranges_deserialized->nranges,
+ ranges_deserialized->nvalues,
+ ranges_deserialized->maxvalues);
+
+ /* serialize ranges */
+ idx = 0;
+ for (i = 0; i < ranges_deserialized->nranges; i++)
+ {
+ Datum a, b;
+ text *c;
+ StringInfoData str;
+
+ initStringInfo(&str);
+
+ a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]);
+ b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]);
+
+ appendStringInfo(&str, "%s ... %s",
+ DatumGetPointer(a),
+ DatumGetPointer(b));
+
+ c = cstring_to_text(str.data);
+
+ astate_values = accumArrayResult(astate_values,
+ PointerGetDatum(c),
+ false,
+ TEXTOID,
+ CurrentMemoryContext);
+ }
+
+ if (ranges_deserialized->nranges > 0)
+ {
+ Oid typoutput;
+ bool typIsVarlena;
+ Datum val;
+ char *extval;
+
+ getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
+
+ val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+
+ extval = OidOutputFunctionCall(typoutput, val);
+
+ appendStringInfo(&str, " ranges: %s", extval);
+ }
+
+ /* serialize individual values */
+ astate_values = NULL;
+
+ for (i = 0; i < ranges_deserialized->nvalues; i++)
+ {
+ Datum a;
+ text *b;
+ StringInfoData str;
+
+ initStringInfo(&str);
+
+ a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]);
+
+ appendStringInfo(&str, "%s", DatumGetPointer(a));
+
+ b = cstring_to_text(str.data);
+
+ astate_values = accumArrayResult(astate_values,
+ PointerGetDatum(b),
+ false,
+ TEXTOID,
+ CurrentMemoryContext);
+ }
+
+ if (ranges_deserialized->nvalues > 0)
+ {
+ Oid typoutput;
+ bool typIsVarlena;
+ Datum val;
+ char *extval;
+
+ getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
+
+ val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+
+ extval = OidOutputFunctionCall(typoutput, val);
+
+ appendStringInfo(&str, " values: %s", extval);
+ }
+
+
+ appendStringInfoChar(&str, '}');
+
+ PG_RETURN_CSTRING(str.data);
+}
+
+/*
+ * brin_minmax_multi_summary_recv
+ * - binary input routine for type brin_minmax_multi_summary.
+ */
+Datum
+brin_minmax_multi_summary_recv(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "brin_minmax_multi_summary")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * brin_minmax_multi_summary_send
+ * - binary output routine for type brin_minmax_multi_summary.
+ *
+ * BRIN minmax-multi summaries are serialized in a bytea value (although
+ * the type is named differently), so let's just send that.
+ */
+Datum
+brin_minmax_multi_summary_send(PG_FUNCTION_ARGS)
+{
+ return byteasend(fcinfo);
+}
+
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index 46e6b23c87..8bdf837070 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -138,6 +138,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_hasnulls)
anynulls = true;
+ /* if needed, serialize the values before forming the on-disk tuple */
+ if (tuple->bt_columns[keyno].bv_serialize)
+ {
+ tuple->bt_columns[keyno].bv_serialize(
+ tuple->bt_columns[keyno].bv_mem_value,
+ tuple->bt_columns[keyno].bv_values);
+ }
+
for (datumno = 0;
datumno < brdesc->bd_info[keyno]->oi_nstored;
datumno++)
@@ -398,6 +406,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc)
dtuple->bt_columns[i].bv_allnulls = true;
dtuple->bt_columns[i].bv_hasnulls = false;
dtuple->bt_columns[i].bv_values = (Datum *) currdatum;
+
+ dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL);
+ dtuple->bt_columns[i].bv_serialize = NULL;
+ dtuple->bt_columns[i].bv_context = dtuple->bt_context;
+
currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored;
}
@@ -477,6 +490,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple)
dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno];
dtup->bt_columns[keyno].bv_allnulls = false;
+
+ dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL);
+ dtup->bt_columns[keyno].bv_serialize = NULL;
+ dtup->bt_columns[keyno].bv_context = dtup->bt_context;
}
MemoryContextSwitchTo(oldcxt);
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index b02298c0aa..03499281c6 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -24,6 +24,7 @@ typedef struct BrinOptions
bool autosummarize;
double nDistinctPerRange; /* number of distinct values per range */
double falsePositiveRate; /* false positive for bloom filter */
+ int valuesPerRange; /* number of values per range */
} BrinOptions;
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index e3c9d47503..ee4d0706df 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -62,6 +62,9 @@ typedef struct BrinDesc
double bd_nDistinctPerRange;
double bd_falsePositiveRange;
+ /* parameters for multi-minmax indexes */
+ int bd_valuesPerRange;
+
/* per-column info; bd_tupdesc->natts entries long */
BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER];
} BrinDesc;
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index a9ccc3995b..064a93d09b 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,6 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
+typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
@@ -27,6 +28,9 @@ typedef struct BrinValues
bool bv_hasnulls; /* are there any nulls in the page range? */
bool bv_allnulls; /* are all values nulls in the page range? */
Datum *bv_values; /* current accumulated values */
+ Datum bv_mem_value; /* expanded accumulated values */
+ MemoryContext bv_context;
+ brin_serialize_callback_type bv_serialize;
} BrinValues;
/*
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 2f1f144db4..d3d12a7b99 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest)
* ----------
*/
#define FirstGenbkiObjectId 10000
-#define FirstBootstrapObjectId 12000
+#define FirstBootstrapObjectId 13000
#define FirstNormalObjectId 16384
/*
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 12033d48ec..cda1ad4fbe 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1900,6 +1900,152 @@
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
amopmethod => 'brin' },
+# minmax multi integer
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int8,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int8,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int8,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int8,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int8,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int8,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int8,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int8,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int8,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int8,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int8,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int8,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int2,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int2,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int2,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int2,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int2,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int2,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int2,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int2,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int2,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int2,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int2,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int2,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '1', amopopr => '<(int4,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '2', amopopr => '<=(int4,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '3', amopopr => '=(int4,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '4', amopopr => '>=(int4,int4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '1', amopopr => '<(int4,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '2', amopopr => '<=(int4,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '3', amopopr => '=(int4,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '4', amopopr => '>=(int4,int2)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '1', amopopr => '<(int4,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '2', amopopr => '<=(int4,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '3', amopopr => '=(int4,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '4', amopopr => '>=(int4,int8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/integer_minmax_multi_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
+ amopmethod => 'brin' },
+
# bloom integer
{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int8',
@@ -1977,6 +2123,23 @@
amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)',
amopmethod => 'brin' },
+# minmax multi oid
+{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '1', amopopr => '<(oid,oid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '2', amopopr => '<=(oid,oid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '3', amopopr => '=(oid,oid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '4', amopopr => '>=(oid,oid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/oid_minmax_multi_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '5', amopopr => '>(oid,oid)',
+ amopmethod => 'brin' },
+
# bloom oid
{ amopfamily => 'brin/oid_bloom_ops', amoplefttype => 'oid',
amoprighttype => 'oid', amopstrategy => '1', amopopr => '=(oid,oid)',
@@ -2003,6 +2166,22 @@
{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid',
amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)',
amopmethod => 'brin' },
+# minmax multi tid
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+ amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)',
+ amopmethod => 'brin' },
# minmax float (float4, float8)
@@ -2070,6 +2249,72 @@
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
amopmethod => 'brin' },
+# minmax multi float (float4, float8)
+
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float4,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '2',
+ amopopr => '<=(float4,float4)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float4,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '4',
+ amopopr => '>=(float4,float4)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float4,float8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '2',
+ amopopr => '<=(float4,float8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float4,float8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '4',
+ amopopr => '>=(float4,float8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '1', amopopr => '<(float8,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '2',
+ amopopr => '<=(float8,float4)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '3', amopopr => '=(float8,float4)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '4',
+ amopopr => '>=(float8,float4)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '1', amopopr => '<(float8,float8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '2',
+ amopopr => '<=(float8,float8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '3', amopopr => '=(float8,float8)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '4',
+ amopopr => '>=(float8,float8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/float_minmax_multi_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
+ amopmethod => 'brin' },
+
# bloom float
{ amopfamily => 'brin/float_bloom_ops', amoplefttype => 'float4',
amoprighttype => 'float4', amopstrategy => '1', amopopr => '=(float4,float4)',
@@ -2101,6 +2346,23 @@
amoprighttype => 'macaddr', amopstrategy => '5',
amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' },
+# minmax multi macaddr
+{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '1',
+ amopopr => '<(macaddr,macaddr)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '2',
+ amopopr => '<=(macaddr,macaddr)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '3',
+ amopopr => '=(macaddr,macaddr)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '4',
+ amopopr => '>=(macaddr,macaddr)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr_minmax_multi_ops', amoplefttype => 'macaddr',
+ amoprighttype => 'macaddr', amopstrategy => '5',
+ amopopr => '>(macaddr,macaddr)', amopmethod => 'brin' },
+
# bloom macaddr
{ amopfamily => 'brin/macaddr_bloom_ops', amoplefttype => 'macaddr',
amoprighttype => 'macaddr', amopstrategy => '1',
@@ -2123,6 +2385,23 @@
amoprighttype => 'macaddr8', amopstrategy => '5',
amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' },
+# minmax multi macaddr8
+{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '1',
+ amopopr => '<(macaddr8,macaddr8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '2',
+ amopopr => '<=(macaddr8,macaddr8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '3',
+ amopopr => '=(macaddr8,macaddr8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '4',
+ amopopr => '>=(macaddr8,macaddr8)', amopmethod => 'brin' },
+{ amopfamily => 'brin/macaddr8_minmax_multi_ops', amoplefttype => 'macaddr8',
+ amoprighttype => 'macaddr8', amopstrategy => '5',
+ amopopr => '>(macaddr8,macaddr8)', amopmethod => 'brin' },
+
# bloom macaddr8
{ amopfamily => 'brin/macaddr8_bloom_ops', amoplefttype => 'macaddr8',
amoprighttype => 'macaddr8', amopstrategy => '1',
@@ -2145,6 +2424,23 @@
amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)',
amopmethod => 'brin' },
+# minmax multi inet
+{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '2', amopopr => '<=(inet,inet)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '3', amopopr => '=(inet,inet)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '4', amopopr => '>=(inet,inet)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/network_minmax_multi_ops', amoplefttype => 'inet',
+ amoprighttype => 'inet', amopstrategy => '5', amopopr => '>(inet,inet)',
+ amopmethod => 'brin' },
+
# bloom inet
{ amopfamily => 'brin/network_bloom_ops', amoplefttype => 'inet',
amoprighttype => 'inet', amopstrategy => '1', amopopr => '=(inet,inet)',
@@ -2209,6 +2505,23 @@
amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
amopmethod => 'brin' },
+# minmax multi time without time zone
+{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '1', amopopr => '<(time,time)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '2', amopopr => '<=(time,time)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '3', amopopr => '=(time,time)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '4', amopopr => '>=(time,time)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/time_minmax_multi_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
+ amopmethod => 'brin' },
+
# bloom time without time zone
{ amopfamily => 'brin/time_bloom_ops', amoplefttype => 'time',
amoprighttype => 'time', amopstrategy => '1', amopopr => '=(time,time)',
@@ -2360,6 +2673,152 @@
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' },
+# minmax multi datetime (date, timestamp, timestamptz)
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '<(timestamp,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '2',
+ amopopr => '<=(timestamp,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '3',
+ amopopr => '=(timestamp,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '4',
+ amopopr => '>=(timestamp,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '5',
+ amopopr => '>(timestamp,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '1', amopopr => '<(timestamp,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(timestamp,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '3', amopopr => '=(timestamp,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(timestamp,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '<(timestamp,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '2',
+ amopopr => '<=(timestamp,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '3',
+ amopopr => '=(timestamp,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '4',
+ amopopr => '>=(timestamp,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '5',
+ amopopr => '>(timestamp,timestamptz)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '1', amopopr => '<(date,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '2', amopopr => '<=(date,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '3', amopopr => '=(date,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '4', amopopr => '>=(date,date)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)',
+ amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '<(date,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '2',
+ amopopr => '<=(date,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '3',
+ amopopr => '=(date,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '4',
+ amopopr => '>=(date,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '5',
+ amopopr => '>(date,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '<(date,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '2',
+ amopopr => '<=(date,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '3',
+ amopopr => '=(date,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '4',
+ amopopr => '>=(date,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '5',
+ amopopr => '>(date,timestamptz)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '1',
+ amopopr => '<(timestamptz,date)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '2',
+ amopopr => '<=(timestamptz,date)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '3',
+ amopopr => '=(timestamptz,date)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '4',
+ amopopr => '>=(timestamptz,date)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '5',
+ amopopr => '>(timestamptz,date)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '1',
+ amopopr => '<(timestamptz,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '2',
+ amopopr => '<=(timestamptz,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '3',
+ amopopr => '=(timestamptz,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '4',
+ amopopr => '>=(timestamptz,timestamp)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '5',
+ amopopr => '>(timestamptz,timestamp)', amopmethod => 'brin' },
+
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '1',
+ amopopr => '<(timestamptz,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '2',
+ amopopr => '<=(timestamptz,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '3',
+ amopopr => '=(timestamptz,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '4',
+ amopopr => '>=(timestamptz,timestamptz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/datetime_minmax_multi_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '5',
+ amopopr => '>(timestamptz,timestamptz)', amopmethod => 'brin' },
+
# bloom datetime (date, timestamp, timestamptz)
{ amopfamily => 'brin/datetime_bloom_ops', amoplefttype => 'timestamp',
@@ -2415,6 +2874,23 @@
amoprighttype => 'interval', amopstrategy => '5',
amopopr => '>(interval,interval)', amopmethod => 'brin' },
+# minmax multi interval
+{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '1',
+ amopopr => '<(interval,interval)', amopmethod => 'brin' },
+{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '2',
+ amopopr => '<=(interval,interval)', amopmethod => 'brin' },
+{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '3',
+ amopopr => '=(interval,interval)', amopmethod => 'brin' },
+{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '4',
+ amopopr => '>=(interval,interval)', amopmethod => 'brin' },
+{ amopfamily => 'brin/interval_minmax_multi_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '5',
+ amopopr => '>(interval,interval)', amopmethod => 'brin' },
+
# bloom interval
{ amopfamily => 'brin/interval_bloom_ops', amoplefttype => 'interval',
amoprighttype => 'interval', amopstrategy => '1',
@@ -2437,6 +2913,23 @@
amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)',
amopmethod => 'brin' },
+# minmax multi time with time zone
+{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '2',
+ amopopr => '<=(timetz,timetz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '3', amopopr => '=(timetz,timetz)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '4',
+ amopopr => '>=(timetz,timetz)', amopmethod => 'brin' },
+{ amopfamily => 'brin/timetz_minmax_multi_ops', amoplefttype => 'timetz',
+ amoprighttype => 'timetz', amopstrategy => '5', amopopr => '>(timetz,timetz)',
+ amopmethod => 'brin' },
+
# bloom time with time zone
{ amopfamily => 'brin/timetz_bloom_ops', amoplefttype => 'timetz',
amoprighttype => 'timetz', amopstrategy => '1', amopopr => '=(timetz,timetz)',
@@ -2493,6 +2986,23 @@
amoprighttype => 'numeric', amopstrategy => '5',
amopopr => '>(numeric,numeric)', amopmethod => 'brin' },
+# minmax multi numeric
+{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '1',
+ amopopr => '<(numeric,numeric)', amopmethod => 'brin' },
+{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '2',
+ amopopr => '<=(numeric,numeric)', amopmethod => 'brin' },
+{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '3',
+ amopopr => '=(numeric,numeric)', amopmethod => 'brin' },
+{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '4',
+ amopopr => '>=(numeric,numeric)', amopmethod => 'brin' },
+{ amopfamily => 'brin/numeric_minmax_multi_ops', amoplefttype => 'numeric',
+ amoprighttype => 'numeric', amopstrategy => '5',
+ amopopr => '>(numeric,numeric)', amopmethod => 'brin' },
+
# bloom numeric
{ amopfamily => 'brin/numeric_bloom_ops', amoplefttype => 'numeric',
amoprighttype => 'numeric', amopstrategy => '1',
@@ -2515,6 +3025,23 @@
amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)',
amopmethod => 'brin' },
+# minmax multi uuid
+{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '2', amopopr => '<=(uuid,uuid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '3', amopopr => '=(uuid,uuid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '4', amopopr => '>=(uuid,uuid)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/uuid_minmax_multi_ops', amoplefttype => 'uuid',
+ amoprighttype => 'uuid', amopstrategy => '5', amopopr => '>(uuid,uuid)',
+ amopmethod => 'brin' },
+
# bloom uuid
{ amopfamily => 'brin/uuid_bloom_ops', amoplefttype => 'uuid',
amoprighttype => 'uuid', amopstrategy => '1', amopopr => '=(uuid,uuid)',
@@ -2581,6 +3108,23 @@
amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)',
amopmethod => 'brin' },
+# minmax multi pg_lsn
+{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '<(pg_lsn,pg_lsn)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '2',
+ amopopr => '<=(pg_lsn,pg_lsn)', amopmethod => 'brin' },
+{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '3', amopopr => '=(pg_lsn,pg_lsn)',
+ amopmethod => 'brin' },
+{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '4',
+ amopopr => '>=(pg_lsn,pg_lsn)', amopmethod => 'brin' },
+{ amopfamily => 'brin/pg_lsn_minmax_multi_ops', amoplefttype => 'pg_lsn',
+ amoprighttype => 'pg_lsn', amopstrategy => '5', amopopr => '>(pg_lsn,pg_lsn)',
+ amopmethod => 'brin' },
+
# bloom pg_lsn
{ amopfamily => 'brin/pg_lsn_bloom_ops', amoplefttype => 'pg_lsn',
amoprighttype => 'pg_lsn', amopstrategy => '1', amopopr => '=(pg_lsn,pg_lsn)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 4e35bb3459..082fbe10b5 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -951,6 +951,152 @@
{ amprocfamily => 'brin/integer_minmax_ops', amproclefttype => 'int4',
amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_union' },
+# minmax multi integer: int2, int4, int8
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int2' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int8', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int8' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/integer_minmax_multi_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int2', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' },
+
# bloom integer: int2, int4, int8
{ amprocfamily => 'brin/integer_bloom_ops', amproclefttype => 'int8',
amprocrighttype => 'int8', amprocnum => '1',
@@ -1046,6 +1192,23 @@
{ amprocfamily => 'brin/oid_minmax_ops', amproclefttype => 'oid',
amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_union' },
+# minmax multi oid
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/oid_minmax_multi_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_int4' },
+
# bloom oid
{ amprocfamily => 'brin/oid_bloom_ops', amproclefttype => 'oid',
amprocrighttype => 'oid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' },
@@ -1092,6 +1255,23 @@
{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' },
+# minmax multi tid
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' },
+
# minmax float
{ amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4',
amprocrighttype => 'float4', amprocnum => '1',
@@ -1142,6 +1322,80 @@
amprocrighttype => 'float4', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi float
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float4', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float4' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float4',
+ amprocrighttype => 'float8', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float8', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/float_minmax_multi_ops', amproclefttype => 'float8',
+ amprocrighttype => 'float4', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+
# bloom float
{ amprocfamily => 'brin/float_bloom_ops', amproclefttype => 'float4',
amprocrighttype => 'float4', amprocnum => '1',
@@ -1195,6 +1449,26 @@
amprocrighttype => 'macaddr', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi macaddr
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/macaddr_minmax_multi_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_macaddr' },
+
# bloom macaddr
{ amprocfamily => 'brin/macaddr_bloom_ops', amproclefttype => 'macaddr',
amprocrighttype => 'macaddr', amprocnum => '1',
@@ -1229,6 +1503,26 @@
amprocrighttype => 'macaddr8', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi macaddr8
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/macaddr8_minmax_multi_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_macaddr8' },
+
# bloom macaddr8
{ amprocfamily => 'brin/macaddr8_bloom_ops', amproclefttype => 'macaddr8',
amprocrighttype => 'macaddr8', amprocnum => '1',
@@ -1262,6 +1556,26 @@
{ amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '4', amproc => 'brin_minmax_union' },
+# minmax multi inet
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/network_minmax_multi_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_inet' },
+
# bloom inet
{ amprocfamily => 'brin/network_bloom_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '1',
@@ -1347,6 +1661,25 @@
{ amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time',
amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_union' },
+# minmax multi time without time zone
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/time_minmax_multi_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_time' },
+
# bloom time without time zone
{ amprocfamily => 'brin/time_bloom_ops', amproclefttype => 'time',
amprocrighttype => 'time', amprocnum => '1',
@@ -1472,6 +1805,170 @@
amprocrighttype => 'timestamptz', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi datetime (date, timestamp, timestamptz)
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamptz', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'date', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamp', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'date', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamp', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+
# bloom datetime (date, timestamp, timestamptz)
{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
amprocrighttype => 'timestamp', amprocnum => '1',
@@ -1542,6 +2039,26 @@
amprocrighttype => 'interval', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi interval
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/interval_minmax_multi_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_interval' },
+
# bloom interval
{ amprocfamily => 'brin/interval_bloom_ops', amproclefttype => 'interval',
amprocrighttype => 'interval', amprocnum => '1',
@@ -1576,6 +2093,26 @@
amprocrighttype => 'timetz', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi time with time zone
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/timetz_minmax_multi_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_timetz' },
+
# bloom time with time zone
{ amprocfamily => 'brin/timetz_bloom_ops', amproclefttype => 'timetz',
amprocrighttype => 'timetz', amprocnum => '1',
@@ -1636,6 +2173,26 @@
amprocrighttype => 'numeric', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi numeric
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/numeric_minmax_multi_ops', amproclefttype => 'numeric',
+ amprocrighttype => 'numeric', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_numeric' },
+
# bloom numeric
{ amprocfamily => 'brin/numeric_bloom_ops', amproclefttype => 'numeric',
amprocrighttype => 'numeric', amprocnum => '1',
@@ -1667,7 +2224,28 @@
amprocrighttype => 'uuid', amprocnum => '3',
amproc => 'brin_minmax_consistent' },
{ amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid',
- amprocrighttype => 'uuid', amprocnum => '4', amproc => 'brin_minmax_union' },
+ amprocrighttype => 'uuid', amprocnum => '4',
+ amproc => 'brin_minmax_union' },
+
+# minmax multi uuid
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/uuid_minmax_multi_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_uuid' },
# bloom uuid
{ amprocfamily => 'brin/uuid_bloom_ops', amproclefttype => 'uuid',
@@ -1722,6 +2300,26 @@
amprocrighttype => 'pg_lsn', amprocnum => '4',
amproc => 'brin_minmax_union' },
+# minmax multi pg_lsn
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/pg_lsn_minmax_multi_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_pg_lsn' },
+
# bloom pg_lsn
{ amprocfamily => 'brin/pg_lsn_bloom_ops', amproclefttype => 'pg_lsn',
amprocrighttype => 'pg_lsn', amprocnum => '1',
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ca747a03b9..27ef11b81b 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -275,18 +275,27 @@
{ opcmethod => 'brin', opcname => 'int8_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8',
opckeytype => 'int8' },
+{ opcmethod => 'brin', opcname => 'int8_minmax_multi_ops',
+ opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int8',
+ opckeytype => 'int8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int8_bloom_ops',
opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8',
opckeytype => 'int8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int2_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2',
opckeytype => 'int2' },
+{ opcmethod => 'brin', opcname => 'int2_minmax_multi_ops',
+ opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int2',
+ opckeytype => 'int2', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int2_bloom_ops',
opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2',
opckeytype => 'int2', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int4_minmax_ops',
opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4',
opckeytype => 'int4' },
+{ opcmethod => 'brin', opcname => 'int4_minmax_multi_ops',
+ opcfamily => 'brin/integer_minmax_multi_ops', opcintype => 'int4',
+ opckeytype => 'int4', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'int4_bloom_ops',
opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4',
opckeytype => 'int4', opcdefault => 'f' },
@@ -298,6 +307,9 @@
opckeytype => 'text', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'oid_minmax_ops',
opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' },
+{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops',
+ opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid',
+ opckeytype => 'oid', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'oid_bloom_ops',
opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid',
opckeytype => 'oid', opcdefault => 'f' },
@@ -306,33 +318,51 @@
{ opcmethod => 'brin', opcname => 'tid_bloom_ops',
opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid',
opcdefault => 'f'},
+{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops',
+ opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid',
+ opckeytype => 'tid', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'float4_minmax_ops',
opcfamily => 'brin/float_minmax_ops', opcintype => 'float4',
opckeytype => 'float4' },
+{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops',
+ opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4',
+ opckeytype => 'float4', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'float4_bloom_ops',
opcfamily => 'brin/float_bloom_ops', opcintype => 'float4',
opckeytype => 'float4', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'float8_minmax_ops',
opcfamily => 'brin/float_minmax_ops', opcintype => 'float8',
opckeytype => 'float8' },
+{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops',
+ opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8',
+ opckeytype => 'float8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'float8_bloom_ops',
opcfamily => 'brin/float_bloom_ops', opcintype => 'float8',
opckeytype => 'float8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr_minmax_ops',
opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr',
opckeytype => 'macaddr' },
+{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops',
+ opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr',
+ opckeytype => 'macaddr', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops',
opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr',
opckeytype => 'macaddr', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr8_minmax_ops',
opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8',
opckeytype => 'macaddr8' },
+{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops',
+ opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8',
+ opckeytype => 'macaddr8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops',
opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8',
opckeytype => 'macaddr8', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'inet_minmax_ops',
opcfamily => 'brin/network_minmax_ops', opcintype => 'inet',
opcdefault => 'f', opckeytype => 'inet' },
+{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops',
+ opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet',
+ opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'inet_bloom_ops',
opcfamily => 'brin/network_bloom_ops', opcintype => 'inet',
opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' },
@@ -348,36 +378,54 @@
{ opcmethod => 'brin', opcname => 'time_minmax_ops',
opcfamily => 'brin/time_minmax_ops', opcintype => 'time',
opckeytype => 'time' },
+{ opcmethod => 'brin', opcname => 'time_minmax_multi_ops',
+ opcfamily => 'brin/time_minmax_multi_ops', opcintype => 'time',
+ opckeytype => 'time', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'time_bloom_ops',
opcfamily => 'brin/time_bloom_ops', opcintype => 'time',
opckeytype => 'time', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'date_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date',
opckeytype => 'date' },
+{ opcmethod => 'brin', opcname => 'date_minmax_multi_ops',
+ opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'date',
+ opckeytype => 'date', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'date_bloom_ops',
opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date',
opckeytype => 'date', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamp_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp',
opckeytype => 'timestamp' },
+{ opcmethod => 'brin', opcname => 'timestamp_minmax_multi_ops',
+ opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamp',
+ opckeytype => 'timestamp', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops',
opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp',
opckeytype => 'timestamp', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamptz_minmax_ops',
opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz',
opckeytype => 'timestamptz' },
+{ opcmethod => 'brin', opcname => 'timestamptz_minmax_multi_ops',
+ opcfamily => 'brin/datetime_minmax_multi_ops', opcintype => 'timestamptz',
+ opckeytype => 'timestamptz', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops',
opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz',
opckeytype => 'timestamptz', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'interval_minmax_ops',
opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval',
opckeytype => 'interval' },
+{ opcmethod => 'brin', opcname => 'interval_minmax_multi_ops',
+ opcfamily => 'brin/interval_minmax_multi_ops', opcintype => 'interval',
+ opckeytype => 'interval', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'interval_bloom_ops',
opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval',
opckeytype => 'interval', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timetz_minmax_ops',
opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz',
opckeytype => 'timetz' },
+{ opcmethod => 'brin', opcname => 'timetz_minmax_multi_ops',
+ opcfamily => 'brin/timetz_minmax_multi_ops', opcintype => 'timetz',
+ opckeytype => 'timetz', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'timetz_bloom_ops',
opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz',
opckeytype => 'timetz', opcdefault => 'f' },
@@ -389,6 +437,9 @@
{ opcmethod => 'brin', opcname => 'numeric_minmax_ops',
opcfamily => 'brin/numeric_minmax_ops', opcintype => 'numeric',
opckeytype => 'numeric' },
+{ opcmethod => 'brin', opcname => 'numeric_minmax_multi_ops',
+ opcfamily => 'brin/numeric_minmax_multi_ops', opcintype => 'numeric',
+ opckeytype => 'numeric', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'numeric_bloom_ops',
opcfamily => 'brin/numeric_bloom_ops', opcintype => 'numeric',
opckeytype => 'numeric', opcdefault => 'f' },
@@ -398,6 +449,9 @@
{ opcmethod => 'brin', opcname => 'uuid_minmax_ops',
opcfamily => 'brin/uuid_minmax_ops', opcintype => 'uuid',
opckeytype => 'uuid' },
+{ opcmethod => 'brin', opcname => 'uuid_minmax_multi_ops',
+ opcfamily => 'brin/uuid_minmax_multi_ops', opcintype => 'uuid',
+ opckeytype => 'uuid', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'uuid_bloom_ops',
opcfamily => 'brin/uuid_bloom_ops', opcintype => 'uuid',
opckeytype => 'uuid', opcdefault => 'f' },
@@ -407,6 +461,9 @@
{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_ops',
opcfamily => 'brin/pg_lsn_minmax_ops', opcintype => 'pg_lsn',
opckeytype => 'pg_lsn' },
+{ opcmethod => 'brin', opcname => 'pg_lsn_minmax_multi_ops',
+ opcfamily => 'brin/pg_lsn_minmax_multi_ops', opcintype => 'pg_lsn',
+ opckeytype => 'pg_lsn', opcdefault => 'f' },
{ opcmethod => 'brin', opcname => 'pg_lsn_bloom_ops',
opcfamily => 'brin/pg_lsn_bloom_ops', opcintype => 'pg_lsn',
opckeytype => 'pg_lsn', opcdefault => 'f' },
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 8875079698..8e6d9eec16 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -180,10 +180,14 @@
opfmethod => 'gin', opfname => 'jsonb_path_ops' },
{ oid => '4054',
opfmethod => 'brin', opfname => 'integer_minmax_ops' },
+{ oid => '9015',
+ opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' },
{ oid => '8100',
opfmethod => 'brin', opfname => 'integer_bloom_ops' },
{ oid => '4055',
opfmethod => 'brin', opfname => 'numeric_minmax_ops' },
+{ oid => '9016',
+ opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' },
{ oid => '4056',
opfmethod => 'brin', opfname => 'text_minmax_ops' },
{ oid => '8101',
@@ -192,10 +196,14 @@
opfmethod => 'brin', opfname => 'numeric_bloom_ops' },
{ oid => '4058',
opfmethod => 'brin', opfname => 'timetz_minmax_ops' },
+{ oid => '9017',
+ opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' },
{ oid => '8103',
opfmethod => 'brin', opfname => 'timetz_bloom_ops' },
{ oid => '4059',
opfmethod => 'brin', opfname => 'datetime_minmax_ops' },
+{ oid => '9018',
+ opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' },
{ oid => '8104',
opfmethod => 'brin', opfname => 'datetime_bloom_ops' },
{ oid => '4062',
@@ -212,26 +220,38 @@
opfmethod => 'brin', opfname => 'name_bloom_ops' },
{ oid => '4068',
opfmethod => 'brin', opfname => 'oid_minmax_ops' },
+{ oid => '9019',
+ opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' },
{ oid => '8108',
opfmethod => 'brin', opfname => 'oid_bloom_ops' },
{ oid => '4069',
opfmethod => 'brin', opfname => 'tid_minmax_ops' },
{ oid => '8123',
opfmethod => 'brin', opfname => 'tid_bloom_ops' },
+{ oid => '9020',
+ opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' },
{ oid => '4070',
opfmethod => 'brin', opfname => 'float_minmax_ops' },
+{ oid => '9021',
+ opfmethod => 'brin', opfname => 'float_minmax_multi_ops' },
{ oid => '8109',
opfmethod => 'brin', opfname => 'float_bloom_ops' },
{ oid => '4074',
opfmethod => 'brin', opfname => 'macaddr_minmax_ops' },
+{ oid => '9022',
+ opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' },
{ oid => '8110',
opfmethod => 'brin', opfname => 'macaddr_bloom_ops' },
{ oid => '4109',
opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' },
+{ oid => '9023',
+ opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' },
{ oid => '8111',
opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' },
{ oid => '4075',
opfmethod => 'brin', opfname => 'network_minmax_ops' },
+{ oid => '9024',
+ opfmethod => 'brin', opfname => 'network_minmax_multi_ops' },
{ oid => '4102',
opfmethod => 'brin', opfname => 'network_inclusion_ops' },
{ oid => '8112',
@@ -242,10 +262,14 @@
opfmethod => 'brin', opfname => 'bpchar_bloom_ops' },
{ oid => '4077',
opfmethod => 'brin', opfname => 'time_minmax_ops' },
+{ oid => '9025',
+ opfmethod => 'brin', opfname => 'time_minmax_multi_ops' },
{ oid => '8114',
opfmethod => 'brin', opfname => 'time_bloom_ops' },
{ oid => '4078',
opfmethod => 'brin', opfname => 'interval_minmax_ops' },
+{ oid => '9026',
+ opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' },
{ oid => '8115',
opfmethod => 'brin', opfname => 'interval_bloom_ops' },
{ oid => '4079',
@@ -254,12 +278,16 @@
opfmethod => 'brin', opfname => 'varbit_minmax_ops' },
{ oid => '4081',
opfmethod => 'brin', opfname => 'uuid_minmax_ops' },
+{ oid => '9027',
+ opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' },
{ oid => '8116',
opfmethod => 'brin', opfname => 'uuid_bloom_ops' },
{ oid => '4103',
opfmethod => 'brin', opfname => 'range_inclusion_ops' },
{ oid => '4082',
opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' },
+{ oid => '9028',
+ opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' },
{ oid => '8117',
opfmethod => 'brin', opfname => 'pg_lsn_bloom_ops' },
{ oid => '4104',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index dc17d4ce38..77735c3abe 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8110,6 +8110,71 @@
proname => 'brin_minmax_union', prorettype => 'bool',
proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' },
+# BRIN minmax multi
+{ oid => '9029', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal',
+ proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' },
+{ oid => '9030', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_add_value', prorettype => 'bool',
+ proargtypes => 'internal internal internal internal',
+ prosrc => 'brin_minmax_multi_add_value' },
+{ oid => '9031', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_consistent', prorettype => 'bool',
+ proargtypes => 'internal internal internal int4',
+ prosrc => 'brin_minmax_multi_consistent' },
+{ oid => '9032', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_union', prorettype => 'bool',
+ proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' },
+{ oid => '9033', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f',
+ proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' },
+
+{ oid => '9000', descr => 'BRIN multi minmax int2 distance',
+ proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' },
+{ oid => '9001', descr => 'BRIN multi minmax int4 distance',
+ proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' },
+{ oid => '9002', descr => 'BRIN multi minmax int8 distance',
+ proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' },
+{ oid => '9003', descr => 'BRIN multi minmax float4 distance',
+ proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' },
+{ oid => '9004', descr => 'BRIN multi minmax float8 distance',
+ proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' },
+{ oid => '9005', descr => 'BRIN multi minmax numeric distance',
+ proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' },
+{ oid => '9006', descr => 'BRIN multi minmax tid distance',
+ proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' },
+{ oid => '9007', descr => 'BRIN multi minmax uuid distance',
+ proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' },
+{ oid => '9008', descr => 'BRIN multi minmax time distance',
+ proname => 'brin_minmax_multi_distance_time', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' },
+{ oid => '9009', descr => 'BRIN multi minmax interval distance',
+ proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' },
+{ oid => '9010', descr => 'BRIN multi minmax timetz distance',
+ proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' },
+{ oid => '9011', descr => 'BRIN multi minmax pg_lsn distance',
+ proname => 'brin_minmax_multi_distance_pg_lsn', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_pg_lsn' },
+{ oid => '9012', descr => 'BRIN multi minmax macaddr distance',
+ proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' },
+{ oid => '9013', descr => 'BRIN multi minmax macaddr8 distance',
+ proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' },
+{ oid => '9014', descr => 'BRIN multi minmax inet distance',
+ proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' },
+
# BRIN inclusion
{ oid => '4105', descr => 'BRIN inclusion support',
proname => 'brin_inclusion_opcinfo', prorettype => 'internal',
@@ -11007,3 +11072,18 @@
{ oid => '9038', descr => 'I/O',
proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea',
proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' },
+
+
+{ oid => '9040', descr => 'I/O',
+ proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary',
+ proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' },
+{ oid => '9041', descr => 'I/O',
+ proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring',
+ proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' },
+{ oid => '9042', descr => 'I/O',
+ proname => 'brin_minmax_multi_summary_recv', provolatile => 's',
+ prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal',
+ prosrc => 'brin_minmax_multi_summary_recv' },
+{ oid => '9043', descr => 'I/O',
+ proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index a41c2e5418..c189b35a3d 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -638,3 +638,10 @@
typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out',
typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send',
typalign => 'i', typstorage => 'x', typcollation => 'default' },
+
+{ oid => '9039', oid_symbol => 'BRINMINMAXMULTISUMMARYOID',
+ descr => 'BRIN minmax-multi summary',
+ typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S',
+ typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out',
+ typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send',
+ typalign => 'i', typstorage => 'x', typcollation => 'default' },
diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out
new file mode 100644
index 0000000000..966dc4aadc
--- /dev/null
+++ b/src/test/regress/expected/brin_multi.out
@@ -0,0 +1,421 @@
+CREATE TABLE brintest_multi (
+ int8col bigint,
+ int2col smallint,
+ int4col integer,
+ oidcol oid,
+ tidcol tid,
+ float4col real,
+ float8col double precision,
+ macaddrcol macaddr,
+ inetcol inet,
+ cidrcol cidr,
+ datecol date,
+ timecol time without time zone,
+ timestampcol timestamp without time zone,
+ timestamptzcol timestamp with time zone,
+ intervalcol interval,
+ timetzcol time with time zone,
+ numericcol numeric,
+ uuidcol uuid,
+ lsncol pg_lsn
+) WITH (fillfactor=10);
+INSERT INTO brintest_multi SELECT
+ 142857 * tenthous,
+ thousand,
+ twothousand,
+ unique1::oid,
+ format('(%s,%s)', tenthous, twenty)::tid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4/24' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20+02' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 100;
+-- throw in some NULL's and different values
+INSERT INTO brintest_multi (inetcol, cidrcol) SELECT
+ inet 'fe80::6e40:8ff:fea9:8c46' + tenthous,
+ cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous
+FROM tenk1 ORDER BY thousand, tenthous LIMIT 25;
+-- test minmax-multi specific index options
+-- number of values must be >= 16
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops(values_per_range = 15)
+);
+ERROR: value 15 out of bounds for option "values_per_range"
+DETAIL: Valid values are between "16" and "256".
+-- number of values must be <= 256
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops(values_per_range = 257)
+);
+ERROR: value 257 out of bounds for option "values_per_range"
+DETAIL: Valid values are between "16" and "256".
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops,
+ int2col int2_minmax_multi_ops,
+ int4col int4_minmax_multi_ops,
+ oidcol oid_minmax_multi_ops,
+ tidcol tid_minmax_multi_ops,
+ float4col float4_minmax_multi_ops,
+ float8col float8_minmax_multi_ops,
+ macaddrcol macaddr_minmax_multi_ops,
+ inetcol inet_minmax_multi_ops,
+ cidrcol inet_minmax_multi_ops,
+ datecol date_minmax_multi_ops,
+ timecol time_minmax_multi_ops,
+ timestampcol timestamp_minmax_multi_ops,
+ timestamptzcol timestamptz_minmax_multi_ops,
+ intervalcol interval_minmax_multi_ops,
+ timetzcol timetz_minmax_multi_ops,
+ numericcol numeric_minmax_multi_ops,
+ uuidcol uuid_minmax_multi_ops,
+ lsncol pg_lsn_minmax_multi_ops
+) with (pages_per_range = 1);
+CREATE TABLE brinopers_multi (colname name, typ text,
+ op text[], value text[], matches int[],
+ check (cardinality(op) = cardinality(value)),
+ check (cardinality(op) = cardinality(matches)));
+INSERT INTO brinopers_multi VALUES
+ ('int2col', 'int2',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int2col', 'int4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int2col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int2',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('int8col', 'int2',
+ '{>, >=}',
+ '{0, 0}',
+ '{100, 100}'),
+ ('int8col', 'int4',
+ '{>, >=}',
+ '{0, 0}',
+ '{100, 100}'),
+ ('int8col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 1257141600, 1428427143, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('oidcol', 'oid',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 8800, 9999, 9999}',
+ '{100, 100, 1, 100, 100}'),
+ ('tidcol', 'tid',
+ '{>, >=, =, <=, <}',
+ '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}',
+ '{100, 100, 1, 100, 100}'),
+ ('float4col', 'float4',
+ '{>, >=, =, <=, <}',
+ '{0.0103093, 0.0103093, 1, 1, 1}',
+ '{100, 100, 4, 100, 96}'),
+ ('float4col', 'float8',
+ '{>, >=, =, <=, <}',
+ '{0.0103093, 0.0103093, 1, 1, 1}',
+ '{100, 100, 4, 100, 96}'),
+ ('float8col', 'float4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 0, 1.98, 1.98}',
+ '{99, 100, 1, 100, 100}'),
+ ('float8col', 'float8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 0, 1.98, 1.98}',
+ '{99, 100, 1, 100, 100}'),
+ ('macaddrcol', 'macaddr',
+ '{>, >=, =, <=, <}',
+ '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}',
+ '{99, 100, 2, 100, 100}'),
+ ('inetcol', 'inet',
+ '{=, <, <=, >, >=}',
+ '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{1, 100, 100, 125, 125}'),
+ ('inetcol', 'cidr',
+ '{<, <=, >, >=}',
+ '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{100, 100, 125, 125}'),
+ ('cidrcol', 'inet',
+ '{=, <, <=, >, >=}',
+ '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{2, 100, 100, 125, 125}'),
+ ('cidrcol', 'cidr',
+ '{=, <, <=, >, >=}',
+ '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{2, 100, 100, 125, 125}'),
+ ('datecol', 'date',
+ '{>, >=, =, <=, <}',
+ '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}',
+ '{100, 100, 1, 100, 100}'),
+ ('timecol', 'time',
+ '{>, >=, =, <=, <}',
+ '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestampcol', 'timestamp',
+ '{>, >=, =, <=, <}',
+ '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestampcol', 'timestamptz',
+ '{>, >=, =, <=, <}',
+ '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestamptzcol', 'timestamptz',
+ '{>, >=, =, <=, <}',
+ '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}',
+ '{100, 100, 1, 100, 100}'),
+ ('intervalcol', 'interval',
+ '{>, >=, =, <=, <}',
+ '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}',
+ '{100, 100, 1, 100, 100}'),
+ ('timetzcol', 'timetz',
+ '{>, >=, =, <=, <}',
+ '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}',
+ '{99, 100, 2, 100, 100}'),
+ ('numericcol', 'numeric',
+ '{>, >=, =, <=, <}',
+ '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}',
+ '{100, 100, 1, 100, 100}'),
+ ('uuidcol', 'uuid',
+ '{>, >=, =, <=, <}',
+ '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}',
+ '{100, 100, 1, 100, 100}'),
+ ('lsncol', 'pg_lsn',
+ '{>, >=, =, <=, <, IS, IS NOT}',
+ '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}',
+ '{100, 100, 1, 100, 100, 25, 100}');
+DO $x$
+DECLARE
+ r record;
+ r2 record;
+ cond text;
+ idx_ctids tid[];
+ ss_ctids tid[];
+ count int;
+ plan_ok bool;
+ plan_line text;
+BEGIN
+ FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP
+
+ -- prepare the condition
+ IF r.value IS NULL THEN
+ cond := format('%I %s %L', r.colname, r.oper, r.value);
+ ELSE
+ cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ);
+ END IF;
+
+ -- run the query using the brin index
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get bitmap indexscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond)
+ INTO idx_ctids;
+
+ -- run the query using a seqscan
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get seqscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond)
+ INTO ss_ctids;
+
+ -- make sure both return the same results
+ count := array_length(idx_ctids, 1);
+
+ IF NOT (count = array_length(ss_ctids, 1) AND
+ idx_ctids @> ss_ctids AND
+ idx_ctids <@ ss_ctids) THEN
+ -- report the results of each scan to make the differences obvious
+ RAISE WARNING 'something not right in %: count %', r, count;
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP
+ RAISE NOTICE 'seqscan: %', r2;
+ END LOOP;
+
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP
+ RAISE NOTICE 'bitmapscan: %', r2;
+ END LOOP;
+ END IF;
+
+ -- make sure we found expected number of matches
+ IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF;
+ END LOOP;
+END;
+$x$;
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+INSERT INTO brintest_multi SELECT
+ 142857 * tenthous,
+ thousand,
+ twothousand,
+ unique1::oid,
+ format('(%s,%s)', tenthous, twenty)::tid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5;
+SELECT brin_desummarize_range('brinidx_multi', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+VACUUM brintest_multi; -- force a summarization cycle in brinidx
+UPDATE brintest_multi SET int8col = int8col * int4col;
+-- Tests for brin_summarize_new_values
+SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index
+ERROR: "brintest_multi" is not an index
+SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
+ERROR: "tenk1_unique1" is not a BRIN index
+SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected
+ brin_summarize_new_values
+---------------------------
+ 0
+(1 row)
+
+-- Tests for brin_desummarize_range
+SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range
+ERROR: block number out of range: -1
+SELECT brin_desummarize_range('brinidx_multi', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+SELECT brin_desummarize_range('brinidx_multi', 0);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+SELECT brin_desummarize_range('brinidx_multi', 100000000);
+ brin_desummarize_range
+------------------------
+
+(1 row)
+
+-- Test brin_summarize_range
+CREATE TABLE brin_summarize_multi (
+ value int
+) WITH (fillfactor=10, autovacuum_enabled=false);
+CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2);
+-- Fill a few pages
+DO $$
+DECLARE curtid tid;
+BEGIN
+ LOOP
+ INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid;
+ EXIT WHEN curtid > tid '(2, 0)';
+ END LOOP;
+END;
+$$;
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_multi_idx', 0);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- nothing: already summarized
+SELECT brin_summarize_range('brin_summarize_multi_idx', 1);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_multi_idx', 2);
+ brin_summarize_range
+----------------------
+ 1
+(1 row)
+
+-- nothing: page doesn't exist in table
+SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295);
+ brin_summarize_range
+----------------------
+ 0
+(1 row)
+
+-- invalid block number values
+SELECT brin_summarize_range('brin_summarize_multi_idx', -1);
+ERROR: block number out of range: -1
+SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296);
+ERROR: block number out of range: 4294967296
+-- test brin cost estimates behave sanely based on correlation of values
+CREATE TABLE brin_test_multi (a INT, b INT);
+INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x);
+CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2);
+CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2);
+VACUUM ANALYZE brin_test_multi;
+-- Ensure brin index is used when columns are perfectly correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1;
+ QUERY PLAN
+--------------------------------------------------
+ Bitmap Heap Scan on brin_test_multi
+ Recheck Cond: (a = 1)
+ -> Bitmap Index Scan on brin_test_multi_a_idx
+ Index Cond: (a = 1)
+(4 rows)
+
+-- Ensure brin index is not used when values are not correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1;
+ QUERY PLAN
+-----------------------------
+ Seq Scan on brin_test_multi
+ Filter: (b = 1)
+(2 rows)
+
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 72c7598c89..f855633634 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4986,12 +4986,13 @@ List of access methods
(0 rows)
\dAc brin pg*.oid*
- List of operator classes
- AM | Input type | Storage type | Operator class | Default?
-------+------------+--------------+----------------+----------
- brin | oid | | oid_bloom_ops | no
- brin | oid | | oid_minmax_ops | yes
-(2 rows)
+ List of operator classes
+ AM | Input type | Storage type | Operator class | Default?
+------+------------+--------------+----------------------+----------
+ brin | oid | | oid_bloom_ops | no
+ brin | oid | | oid_minmax_multi_ops | no
+ brin | oid | | oid_minmax_ops | yes
+(3 rows)
\dAf spgist
List of operator families
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 97bf9797de..55a30a6b47 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -67,14 +67,15 @@ WHERE p1.typtype not in ('c','d','p') AND p1.typname NOT LIKE E'\\_%'
(SELECT 1 FROM pg_type as p2
WHERE p2.typname = ('_' || p1.typname)::name AND
p2.typelem = p1.oid and p1.typarray = p2.oid);
- oid | typname
-------+-----------------------
+ oid | typname
+------+------------------------------
194 | pg_node_tree
3361 | pg_ndistinct
3402 | pg_dependencies
5017 | pg_mcv_list
9034 | pg_brin_bloom_summary
-(5 rows)
+ 9039 | pg_brin_minmax_multi_summary
+(6 rows)
-- Make sure typarray points to a varlena array type of our own base
SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index b49239b1d0..a933db5456 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -78,7 +78,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview
# ----------
# Additional BRIN tests
# ----------
-test: brin_bloom
+test: brin_bloom brin_multi
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index abce8e180a..3f05a7061f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -108,6 +108,7 @@ test: namespace
test: prepared_xacts
test: brin
test: brin_bloom
+test: brin_multi
test: gin
test: gist
test: spgist
diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql
new file mode 100644
index 0000000000..9de6ddc6d7
--- /dev/null
+++ b/src/test/regress/sql/brin_multi.sql
@@ -0,0 +1,371 @@
+CREATE TABLE brintest_multi (
+ int8col bigint,
+ int2col smallint,
+ int4col integer,
+ oidcol oid,
+ tidcol tid,
+ float4col real,
+ float8col double precision,
+ macaddrcol macaddr,
+ inetcol inet,
+ cidrcol cidr,
+ datecol date,
+ timecol time without time zone,
+ timestampcol timestamp without time zone,
+ timestamptzcol timestamp with time zone,
+ intervalcol interval,
+ timetzcol time with time zone,
+ numericcol numeric,
+ uuidcol uuid,
+ lsncol pg_lsn
+) WITH (fillfactor=10);
+
+INSERT INTO brintest_multi SELECT
+ 142857 * tenthous,
+ thousand,
+ twothousand,
+ unique1::oid,
+ format('(%s,%s)', tenthous, twenty)::tid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4/24' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20+02' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 100;
+
+-- throw in some NULL's and different values
+INSERT INTO brintest_multi (inetcol, cidrcol) SELECT
+ inet 'fe80::6e40:8ff:fea9:8c46' + tenthous,
+ cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous
+FROM tenk1 ORDER BY thousand, tenthous LIMIT 25;
+
+-- test minmax-multi specific index options
+-- number of values must be >= 16
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops(values_per_range = 15)
+);
+-- number of values must be <= 256
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops(values_per_range = 257)
+);
+
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+ int8col int8_minmax_multi_ops,
+ int2col int2_minmax_multi_ops,
+ int4col int4_minmax_multi_ops,
+ oidcol oid_minmax_multi_ops,
+ tidcol tid_minmax_multi_ops,
+ float4col float4_minmax_multi_ops,
+ float8col float8_minmax_multi_ops,
+ macaddrcol macaddr_minmax_multi_ops,
+ inetcol inet_minmax_multi_ops,
+ cidrcol inet_minmax_multi_ops,
+ datecol date_minmax_multi_ops,
+ timecol time_minmax_multi_ops,
+ timestampcol timestamp_minmax_multi_ops,
+ timestamptzcol timestamptz_minmax_multi_ops,
+ intervalcol interval_minmax_multi_ops,
+ timetzcol timetz_minmax_multi_ops,
+ numericcol numeric_minmax_multi_ops,
+ uuidcol uuid_minmax_multi_ops,
+ lsncol pg_lsn_minmax_multi_ops
+) with (pages_per_range = 1);
+
+CREATE TABLE brinopers_multi (colname name, typ text,
+ op text[], value text[], matches int[],
+ check (cardinality(op) = cardinality(value)),
+ check (cardinality(op) = cardinality(matches)));
+
+INSERT INTO brinopers_multi VALUES
+ ('int2col', 'int2',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int2col', 'int4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int2col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 999, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int2',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1999}',
+ '{100, 100, 1, 100, 100}'),
+ ('int4col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 800, 1999, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('int8col', 'int2',
+ '{>, >=}',
+ '{0, 0}',
+ '{100, 100}'),
+ ('int8col', 'int4',
+ '{>, >=}',
+ '{0, 0}',
+ '{100, 100}'),
+ ('int8col', 'int8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 1257141600, 1428427143, 1428427143}',
+ '{100, 100, 1, 100, 100}'),
+ ('oidcol', 'oid',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 8800, 9999, 9999}',
+ '{100, 100, 1, 100, 100}'),
+ ('tidcol', 'tid',
+ '{>, >=, =, <=, <}',
+ '{"(0,0)", "(0,0)", "(8800,0)", "(9999,19)", "(9999,19)"}',
+ '{100, 100, 1, 100, 100}'),
+ ('float4col', 'float4',
+ '{>, >=, =, <=, <}',
+ '{0.0103093, 0.0103093, 1, 1, 1}',
+ '{100, 100, 4, 100, 96}'),
+ ('float4col', 'float8',
+ '{>, >=, =, <=, <}',
+ '{0.0103093, 0.0103093, 1, 1, 1}',
+ '{100, 100, 4, 100, 96}'),
+ ('float8col', 'float4',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 0, 1.98, 1.98}',
+ '{99, 100, 1, 100, 100}'),
+ ('float8col', 'float8',
+ '{>, >=, =, <=, <}',
+ '{0, 0, 0, 1.98, 1.98}',
+ '{99, 100, 1, 100, 100}'),
+ ('macaddrcol', 'macaddr',
+ '{>, >=, =, <=, <}',
+ '{00:00:01:00:00:00, 00:00:01:00:00:00, 2c:00:2d:00:16:00, ff:fe:00:00:00:00, ff:fe:00:00:00:00}',
+ '{99, 100, 2, 100, 100}'),
+ ('inetcol', 'inet',
+ '{=, <, <=, >, >=}',
+ '{10.2.14.231/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{1, 100, 100, 125, 125}'),
+ ('inetcol', 'cidr',
+ '{<, <=, >, >=}',
+ '{255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{100, 100, 125, 125}'),
+ ('cidrcol', 'inet',
+ '{=, <, <=, >, >=}',
+ '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{2, 100, 100, 125, 125}'),
+ ('cidrcol', 'cidr',
+ '{=, <, <=, >, >=}',
+ '{10.2.14/24, 255.255.255.255, 255.255.255.255, 0.0.0.0, 0.0.0.0}',
+ '{2, 100, 100, 125, 125}'),
+ ('datecol', 'date',
+ '{>, >=, =, <=, <}',
+ '{1995-08-15, 1995-08-15, 2009-12-01, 2022-12-30, 2022-12-30}',
+ '{100, 100, 1, 100, 100}'),
+ ('timecol', 'time',
+ '{>, >=, =, <=, <}',
+ '{01:20:30, 01:20:30, 02:28:57, 06:28:31.5, 06:28:31.5}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestampcol', 'timestamp',
+ '{>, >=, =, <=, <}',
+ '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestampcol', 'timestamptz',
+ '{>, >=, =, <=, <}',
+ '{1942-07-23 03:05:09, 1942-07-23 03:05:09, 1964-03-24 19:26:45, 1984-01-20 22:42:21, 1984-01-20 22:42:21}',
+ '{100, 100, 1, 100, 100}'),
+ ('timestamptzcol', 'timestamptz',
+ '{>, >=, =, <=, <}',
+ '{1972-10-10 03:00:00-04, 1972-10-10 03:00:00-04, 1972-10-19 09:00:00-07, 1972-11-20 19:00:00-03, 1972-11-20 19:00:00-03}',
+ '{100, 100, 1, 100, 100}'),
+ ('intervalcol', 'interval',
+ '{>, >=, =, <=, <}',
+ '{00:00:00, 00:00:00, 1 mons 13 days 12:24, 2 mons 23 days 07:48:00, 1 year}',
+ '{100, 100, 1, 100, 100}'),
+ ('timetzcol', 'timetz',
+ '{>, >=, =, <=, <}',
+ '{01:30:20+02, 01:30:20+02, 01:35:50+02, 23:55:05+02, 23:55:05+02}',
+ '{99, 100, 2, 100, 100}'),
+ ('numericcol', 'numeric',
+ '{>, >=, =, <=, <}',
+ '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}',
+ '{100, 100, 1, 100, 100}'),
+ ('uuidcol', 'uuid',
+ '{>, >=, =, <=, <}',
+ '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}',
+ '{100, 100, 1, 100, 100}'),
+ ('lsncol', 'pg_lsn',
+ '{>, >=, =, <=, <, IS, IS NOT}',
+ '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}',
+ '{100, 100, 1, 100, 100, 25, 100}');
+
+DO $x$
+DECLARE
+ r record;
+ r2 record;
+ cond text;
+ idx_ctids tid[];
+ ss_ctids tid[];
+ count int;
+ plan_ok bool;
+ plan_line text;
+BEGIN
+ FOR r IN SELECT colname, oper, typ, value[ordinality], matches[ordinality] FROM brinopers_multi, unnest(op) WITH ORDINALITY AS oper LOOP
+
+ -- prepare the condition
+ IF r.value IS NULL THEN
+ cond := format('%I %s %L', r.colname, r.oper, r.value);
+ ELSE
+ cond := format('%I %s %L::%s', r.colname, r.oper, r.value, r.typ);
+ END IF;
+
+ -- run the query using the brin index
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Bitmap Heap Scan on brintest_multi%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get bitmap indexscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond)
+ INTO idx_ctids;
+
+ -- run the query using a seqscan
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+
+ plan_ok := false;
+ FOR plan_line IN EXECUTE format($y$EXPLAIN SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond) LOOP
+ IF plan_line LIKE '%Seq Scan on brintest_multi%' THEN
+ plan_ok := true;
+ END IF;
+ END LOOP;
+ IF NOT plan_ok THEN
+ RAISE WARNING 'did not get seqscan plan for %', r;
+ END IF;
+
+ EXECUTE format($y$SELECT array_agg(ctid) FROM brintest_multi WHERE %s $y$, cond)
+ INTO ss_ctids;
+
+ -- make sure both return the same results
+ count := array_length(idx_ctids, 1);
+
+ IF NOT (count = array_length(ss_ctids, 1) AND
+ idx_ctids @> ss_ctids AND
+ idx_ctids <@ ss_ctids) THEN
+ -- report the results of each scan to make the differences obvious
+ RAISE WARNING 'something not right in %: count %', r, count;
+ SET enable_seqscan = 1;
+ SET enable_bitmapscan = 0;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP
+ RAISE NOTICE 'seqscan: %', r2;
+ END LOOP;
+
+ SET enable_seqscan = 0;
+ SET enable_bitmapscan = 1;
+ FOR r2 IN EXECUTE 'SELECT ' || r.colname || ' FROM brintest_multi WHERE ' || cond LOOP
+ RAISE NOTICE 'bitmapscan: %', r2;
+ END LOOP;
+ END IF;
+
+ -- make sure we found expected number of matches
+ IF count != r.matches THEN RAISE WARNING 'unexpected number of results % for %', count, r; END IF;
+ END LOOP;
+END;
+$x$;
+
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+
+INSERT INTO brintest_multi SELECT
+ 142857 * tenthous,
+ thousand,
+ twothousand,
+ unique1::oid,
+ format('(%s,%s)', tenthous, twenty)::tid,
+ (four + 1.0)/(hundred+1),
+ odd::float8 / (tenthous + 1),
+ format('%s:00:%s:00:%s:00', to_hex(odd), to_hex(even), to_hex(hundred))::macaddr,
+ inet '10.2.3.4' + tenthous,
+ cidr '10.2.3/24' + tenthous,
+ date '1995-08-15' + tenthous,
+ time '01:20:30' + thousand * interval '18.5 second',
+ timestamp '1942-07-23 03:05:09' + tenthous * interval '36.38 hours',
+ timestamptz '1972-10-10 03:00' + thousand * interval '1 hour',
+ justify_days(justify_hours(tenthous * interval '12 minutes')),
+ timetz '01:30:20' + hundred * interval '15 seconds',
+ tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
+ format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
+ format('%s/%s%s', odd, even, tenthous)::pg_lsn
+FROM tenk1 ORDER BY unique2 LIMIT 5 OFFSET 5;
+
+SELECT brin_desummarize_range('brinidx_multi', 0);
+VACUUM brintest_multi; -- force a summarization cycle in brinidx
+
+UPDATE brintest_multi SET int8col = int8col * int4col;
+
+-- Tests for brin_summarize_new_values
+SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index
+SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
+SELECT brin_summarize_new_values('brinidx_multi'); -- ok, no change expected
+
+-- Tests for brin_desummarize_range
+SELECT brin_desummarize_range('brinidx_multi', -1); -- error, invalid range
+SELECT brin_desummarize_range('brinidx_multi', 0);
+SELECT brin_desummarize_range('brinidx_multi', 0);
+SELECT brin_desummarize_range('brinidx_multi', 100000000);
+
+-- Test brin_summarize_range
+CREATE TABLE brin_summarize_multi (
+ value int
+) WITH (fillfactor=10, autovacuum_enabled=false);
+CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2);
+-- Fill a few pages
+DO $$
+DECLARE curtid tid;
+BEGIN
+ LOOP
+ INSERT INTO brin_summarize_multi VALUES (1) RETURNING ctid INTO curtid;
+ EXIT WHEN curtid > tid '(2, 0)';
+ END LOOP;
+END;
+$$;
+
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_multi_idx', 0);
+-- nothing: already summarized
+SELECT brin_summarize_range('brin_summarize_multi_idx', 1);
+-- summarize one range
+SELECT brin_summarize_range('brin_summarize_multi_idx', 2);
+-- nothing: page doesn't exist in table
+SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295);
+-- invalid block number values
+SELECT brin_summarize_range('brin_summarize_multi_idx', -1);
+SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296);
+
+
+-- test brin cost estimates behave sanely based on correlation of values
+CREATE TABLE brin_test_multi (a INT, b INT);
+INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x);
+CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2);
+CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2);
+VACUUM ANALYZE brin_test_multi;
+
+-- Ensure brin index is used when columns are perfectly correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1;
+-- Ensure brin index is not used when values are not correlated
+EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1;
--
2.25.4
[text/plain] 0005-Ignore-correlation-for-new-BRIN-opclasses-20200912.patch (4.1K, ../20200912161303.z54jctx5dw4srrh3@development/6-0005-Ignore-correlation-for-new-BRIN-opclasses-20200912.patch)
download | inline diff:
From 38f2d03d252595fe007b65fc097cc688b3909931 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 12 Sep 2020 15:07:59 +0200
Subject: [PATCH 5/5] Ignore correlation for new BRIN opclasses
The new BRIN opclasses (bloom and minmax-multi) are less sensitive to
poorly correlated data, so just assume the data is perfectly correlated
during costing.
---
src/backend/access/brin/brin_bloom.c | 1 +
src/backend/access/brin/brin_minmax_multi.c | 1 +
src/backend/utils/adt/selfuncs.c | 19 ++++++++++++++++++-
src/include/access/brin_internal.h | 3 +++
4 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
index c2cbbd9400..03e9d4b713 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/src/backend/access/brin/brin_bloom.c
@@ -681,6 +681,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS)
result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) +
sizeof(BloomOpaque));
+ result->oi_ignore_correlation = true;
result->oi_nstored = 1;
result->oi_regular_nulls = true;
result->oi_opaque = (BloomOpaque *)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 227927fcf5..c8f36b0c8a 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -1306,6 +1306,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) +
sizeof(MinmaxMultiOpaque));
+ result->oi_ignore_correlation = true;
result->oi_nstored = 1;
result->oi_regular_nulls = true;
result->oi_opaque = (MinmaxMultiOpaque *)
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 00c7afc66f..c00265a66d 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -98,6 +98,7 @@
#include <math.h>
#include "access/brin.h"
+#include "access/brin_internal.h"
#include "access/brin_page.h"
#include "access/gin.h"
#include "access/table.h"
@@ -7350,7 +7351,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double minimalRanges;
double estimatedRanges;
double selec;
- Relation indexRel;
+ Relation indexRel = NULL;
+ TupleDesc tupdesc = NULL;
ListCell *l;
VariableStatData vardata;
@@ -7372,6 +7374,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
*/
indexRel = index_open(index->indexoid, NoLock);
brinGetStats(indexRel, &statsData);
+ tupdesc = RelationGetDescr(indexRel);
index_close(indexRel, NoLock);
/* work out the actual number of ranges in the index */
@@ -7405,6 +7408,17 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
{
IndexClause *iclause = lfirst_node(IndexClause, l);
AttrNumber attnum = index->indexkeys[iclause->indexcol];
+ FmgrInfo *opcInfoFn;
+ BrinOpcInfo *opcInfo;
+ Form_pg_attribute attr = TupleDescAttr(tupdesc, iclause->indexcol);
+ bool ignore_correlation;
+
+ opcInfoFn = index_getprocinfo(indexRel, iclause->indexcol + 1, BRIN_PROCNUM_OPCINFO);
+
+ opcInfo = (BrinOpcInfo *)
+ DatumGetPointer(FunctionCall1(opcInfoFn, attr->atttypid));
+
+ ignore_correlation = opcInfo->oi_ignore_correlation;
/* attempt to lookup stats in relation for this index column */
if (attnum != 0)
@@ -7475,6 +7489,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
if (sslot.nnumbers > 0)
varCorrelation = Abs(sslot.numbers[0]);
+ if (ignore_correlation)
+ varCorrelation = 1.0;
+
if (varCorrelation > *indexCorrelation)
*indexCorrelation = varCorrelation;
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index ee4d0706df..67aea62a02 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -30,6 +30,9 @@ typedef struct BrinOpcInfo
/* Regular processing of NULLs in BrinValues? */
bool oi_regular_nulls;
+ /* Ignore correlation during cost estimation */
+ bool oi_ignore_correlation;
+
/* Opaque pointer for the opclass' private use */
void *oi_opaque;
--
2.25.4
view thread (177+ messages) latest in thread
Message-ID: <20200912161303.z54jctx5dw4srrh3@development>
Permalink: ../20200912161303.z54jctx5dw4srrh3@development/
Also on: postgresql.org/message-id/20200912161303.z54jctx5dw4srrh3@development
copy link · copy postgr.es · copy git am
reply Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: tomas.vondra@2ndquadrant.com, john.naylor@2ndquadrant.com, alvherre@2ndquadrant.com
Subject: Re: WIP: BRIN multi-range indexes
In-Reply-To: <20200912161303.z54jctx5dw4srrh3@development>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox