pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feed From: Tomas Vondra <tomas.vondra@2ndquadrant.com>
To: Alexander Korotkov <a.korotkov@postgrespro.ru>
Cc: Michael Paquier <michael@paquier.xyz>
Cc: Alvaro Herrera <alvherre@2ndquadrant.com>
Cc: Tom Lane <tgl@sss.pgh.pa.us>
Cc: Andres Freund <andres@anarazel.de>
Cc: Mark Dilger <hornschnorter@gmail.com>
Cc: pgsql-hackers <pgsql-hackers@postgresql.org>
Subject: Re: WIP: BRIN multi-range indexes
Date: Fri, 3 Jul 2020 02:57:37 +0200
Message-ID: <20200703005737.dw5i2uwd6i3gv5lf@development> (raw )
In-Reply-To: <CAPpHfdtkV=fE-_9B-K_RZaxexksDwRUp6GvfiXRa0DcreRwtQg@mail.gmail.com >
References: <20190914173049.welhipsj7bijqahm@development >
<20190925200748.GA22809@alvherre.pgsql >
<20190926190148.piwdqnfc2brargqq@development >
<20191201015502.GF2355@paquier.xyz >
<20200402022904.xssjyfupdaj2ejyp@development >
<CAPpHfdsaLSaWvmvVO-tz_t7PmeueWctCFq9URzYW1cReUdzKAA@mail.gmail.com >
<20200405155317.rpmkqdj7far7mwkx@development >
<CAPpHfduRd9sZy5M9NYwbvgSLMt4+X104+7J1pvUimq+w66n54A@mail.gmail.com >
<20200405165954.m2odnthc7sbdctwg@development >
<CAPpHfdtkV=fE-_9B-K_RZaxexksDwRUp6GvfiXRa0DcreRwtQg@mail.gmail.com >
On Sun, Apr 05, 2020 at 08:01:50PM +0300, Alexander Korotkov wrote:
>On Sun, Apr 5, 2020 at 8:00 PM Tomas Vondra
><tomas.vondra@2ndquadrant.com> wrote:
...
>> >
>> >Assuming we're not going to get 0001-0003 into v13, I'm not so
>> >inclined to rush on these three as well. But you're willing to commit
>> >them, you can count round of review on me.
>> >
>>
>> I have no intention to get 0001-0003 committed. I think those changes
>> are beneficial on their own, but the primary reason was to support the
>> new opclasses (which require those changes). And those parts are not
>> going to make it into v13 ...
>
>OK, no problem.
>Let's do this for v14.
>
Hi Alexander,
Are you still interested in reviewing those patches? I'll take a look at
0001-0003 to check that your previous feedback was addressed. Do you
have any comments about 0004 / 0005, which I think are the more
interesting parts of this series?
Attached is a rebased version - I realized I forgot to include 0005 in
the last update, for some reason.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From ac8e74149e8ce6c291e8c414872e1fb2903836d0 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Fri, 13 Sep 2019 18:34:39 +0200
Subject: [PATCH 1/5] Pass all keys to BRIN consistent function at once
---
src/backend/access/brin/brin.c | 126 ++++++++++++-----
src/backend/access/brin/brin_inclusion.c | 164 +++++++++++++++--------
src/backend/access/brin/brin_minmax.c | 116 +++++++++++-----
src/backend/access/brin/brin_validate.c | 4 +-
src/include/catalog/pg_proc.dat | 4 +-
5 files changed, 293 insertions(+), 121 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 7db3ae5ee0..f1beb2eff9 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -388,6 +388,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;
@@ -409,6 +412,53 @@ 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;
+
+ 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);
@@ -469,7 +519,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
else
{
- int keyno;
+ int attno;
/*
* Compare scan keys with summary values stored for the range.
@@ -479,34 +529,19 @@ 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])
+ 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
@@ -518,12 +553,43 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
* the range as a whole, so break out of the loop as soon
* as a false return value is obtained.
*/
- 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 */
+ 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..8968886ff5 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,103 @@ 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 matches;
+ 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);
}
-
- /*
- * 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);
-
- /*
- * 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];
+ matches = true;
+
+ 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;
+
+ matches = inclusion_consistent_key(bdesc, column, key, colloid);
+
+ if (!matches)
+ break;
+ }
+
+ PG_RETURN_BOOL(matches);
+}
+
+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 +376,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 +437,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 +458,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 +477,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 +512,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 +522,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..1219a3a2ab 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,99 @@ 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;
+ ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2);
+ int nkeys = PG_GETARG_INT32(3);
+ Oid colloid = PG_GET_COLLATION();
Datum matches;
- FmgrInfo *finfo;
+ int keyno;
+ bool regular_keys = false;
- Assert(key->sk_attno == column->bv_attno);
-
- /* 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);
-
- /*
- * Neither IS NULL nor IS NOT NULL was used; assume all indexable
- * operators are strict and return false.
- */
+ /*
+ * 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);
+
+ matches = true;
+
+ 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;
+
+ matches = minmax_consistent_key(bdesc, column, key, colloid);
+
+ /* found non-matching key */
+ if (!matches)
+ break;
}
- /* if the range is all empty, it cannot possibly be consistent */
- if (column->bv_allnulls)
- PG_RETURN_BOOL(false);
+ PG_RETURN_DATUM(matches);
+}
+
+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 +283,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 38295aca48..46cedaeee6 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8076,7 +8076,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',
@@ -8092,7 +8092,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 1f175a68c2d5332bb86d62036bc8771b233b8791 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 9 Jun 2019 22:05:39 +0200
Subject: [PATCH 2/5] Move IS NOT NULL checks to bringetbitmap
---
src/backend/access/brin/brin.c | 116 ++++++++++++++++++++---
src/backend/access/brin/brin_inclusion.c | 62 +-----------
src/backend/access/brin/brin_minmax.c | 62 +-----------
3 files changed, 109 insertions(+), 131 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index f1beb2eff9..25ae02a999 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -388,8 +388,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;
@@ -414,10 +416,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,14 +444,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
keyattno - 1)->attcollation));
/* First time we see this index attribute, so init as needed. */
- if (!keys[keyattno-1])
+ if (consistentFn[keyattno - 1].fn_oid == InvalidOid)
{
FmgrInfo *tmp;
- keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);
-
- /* First time this column, so look up consistent function */
- Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid);
+ 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);
@@ -454,9 +457,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 */
@@ -543,6 +560,83 @@ 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.
+ */
+ for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++)
+ {
+ ScanKey key = nullkeys[attno - 1][keyno];
+
+ Assert(key->sk_attno == bval->bv_attno);
+
+ /* interrupt the loop as soon as we find a mismatch */
+ if (!addrange)
+ break;
+
+ /* handle IS NULL/IS NOT NULL tests */
+ if (key->sk_flags & SK_ISNULL)
+ {
+ /* IS NULL scan key, but range has no NULLs */
+ if (key->sk_flags & SK_SEARCHNULL)
+ {
+ if (!bval->bv_allnulls && !bval->bv_hasnulls)
+ addrange = false;
+
+ continue;
+ }
+
+ /*
+ * For IS NOT NULL, we can only skip ranges that are
+ * known to have only nulls.
+ */
+ if (key->sk_flags & SK_SEARCHNOTNULL)
+ {
+ if (bval->bv_allnulls)
+ addrange = false;
+
+ continue;
+ }
+
+ /*
+ * 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.
+ */
+ addrange = false;
+ }
+ }
+
+ /*
+ * If any of the IS [NOT] NULL keys failed, the page range as
+ * a whole can't pass. So terminate the loop.
+ */
+ if (!addrange)
+ 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
diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c
index 8968886ff5..22edc6b46f 100644
--- a/src/backend/access/brin/brin_inclusion.c
+++ b/src/backend/access/brin/brin_inclusion.c
@@ -265,63 +265,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
Oid colloid = PG_GET_COLLATION();
int keyno;
bool matches;
- 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]))
@@ -333,9 +276,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));
matches = inclusion_consistent_key(bdesc, column, key, colloid);
diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c
index 1219a3a2ab..7a7bd21cec 100644
--- a/src/backend/access/brin/brin_minmax.c
+++ b/src/backend/access/brin/brin_minmax.c
@@ -153,63 +153,6 @@ brin_minmax_consistent(PG_FUNCTION_ARGS)
Oid colloid = PG_GET_COLLATION();
Datum matches;
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);
matches = true;
@@ -217,9 +160,8 @@ brin_minmax_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));
matches = minmax_consistent_key(bdesc, column, key, colloid);
--
2.25.4
From f77e38ab6997775a75d0ca03fd8c2ced094dceeb Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tv@fuzzy.cz>
Date: Thu, 2 Apr 2020 02:56:00 +0200
Subject: [PATCH 3/5] Move processing of NULLs from BRIN support functions
---
src/backend/access/brin/brin.c | 260 ++++++++++++++---------
src/backend/access/brin/brin_inclusion.c | 44 +---
src/backend/access/brin/brin_minmax.c | 41 +---
src/include/access/brin_internal.h | 3 +
4 files changed, 169 insertions(+), 179 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 25ae02a999..8c94252567 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
@@ -177,7 +180,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
OffsetNumber off;
BrinTuple *brtup;
BrinMemTuple *dtup;
- int keyno;
CHECK_FOR_INTERRUPTS();
@@ -241,31 +243,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)
{
@@ -358,6 +336,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys)
return scan;
}
+
/*
* Execute the index scan.
*
@@ -561,69 +540,31 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
(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.
+ * 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.
*/
- for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++)
+ if (bdesc->bd_info[attno - 1]->oi_regular_nulls &&
+ !check_null_keys(bval, nullkeys[attno - 1],
+ nnullkeys[attno - 1]))
{
- ScanKey key = nullkeys[attno - 1][keyno];
-
- Assert(key->sk_attno == bval->bv_attno);
-
- /* interrupt the loop as soon as we find a mismatch */
- if (!addrange)
- break;
-
- /* handle IS NULL/IS NOT NULL tests */
- if (key->sk_flags & SK_ISNULL)
- {
- /* IS NULL scan key, but range has no NULLs */
- if (key->sk_flags & SK_SEARCHNULL)
- {
- if (!bval->bv_allnulls && !bval->bv_hasnulls)
- addrange = false;
-
- continue;
- }
-
- /*
- * For IS NOT NULL, we can only skip ranges that are
- * known to have only nulls.
- */
- if (key->sk_flags & SK_SEARCHNOTNULL)
- {
- if (bval->bv_allnulls)
- addrange = false;
-
- continue;
- }
-
- /*
- * 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.
- */
- addrange = false;
- }
- }
-
- /*
- * If any of the IS [NOT] NULL keys failed, the page range as
- * a whole can't pass. So terminate the loop.
- */
- if (!addrange)
+ /*
+ * 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).
+ * 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. */
@@ -771,7 +712,6 @@ brinbuildCallback(Relation index,
{
BrinBuildState *state = (BrinBuildState *) brstate;
BlockNumber thisblock;
- int i;
thisblock = ItemPointerGetBlockNumber(tid);
@@ -800,25 +740,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);
}
/*
@@ -1597,6 +1520,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);
@@ -1650,3 +1606,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 22edc6b46f..59503b6f68 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);
@@ -510,37 +500,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 7a7bd21cec..8882eec12c 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);
@@ -245,34 +235,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 9d01bf0157c545bbad5699b38afcaf6adbb820a5 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tv@fuzzy.cz>
Date: Thu, 2 Apr 2020 02:57:59 +0200
Subject: [PATCH 4/5] BRIN bloom indexes
---
doc/src/sgml/brin.sgml | 215 +++++
doc/src/sgml/ref/create_index.sgml | 31 +
src/backend/access/brin/Makefile | 1 +
src/backend/access/brin/brin_bloom.c | 980 +++++++++++++++++++++++
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 4 +
src/include/catalog/pg_amop.dat | 165 ++++
src/include/catalog/pg_amproc.dat | 430 ++++++++++
src/include/catalog/pg_opclass.dat | 69 ++
src/include/catalog/pg_opfamily.dat | 36 +
src/include/catalog/pg_proc.dat | 20 +
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/parallel_schedule | 5 +
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/brin_bloom.sql | 404 ++++++++++
17 files changed, 2823 insertions(+), 2 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 4c5eeb875f..cc3533ef1f 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -132,6 +132,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
</row>
</thead>
<tbody>
+ <row>
+ <entry><literal>int8_bloom_ops</literal></entry>
+ <entry><type>bigint</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>int8_minmax_ops</literal></entry>
<entry><type>bigint</type></entry>
@@ -183,6 +190,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>|&></literal>
</entry>
</row>
+ <row>
+ <entry><literal>bytea_bloom_ops</literal></entry>
+ <entry><type>bytea</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>bytea_minmax_ops</literal></entry>
<entry><type>bytea</type></entry>
@@ -194,6 +208,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>bpchar_bloom_ops</literal></entry>
+ <entry><type>character</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>bpchar_minmax_ops</literal></entry>
<entry><type>character</type></entry>
@@ -205,6 +226,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>char_bloom_ops</literal></entry>
+ <entry><type>"char"</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>char_minmax_ops</literal></entry>
<entry><type>"char"</type></entry>
@@ -216,6 +244,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>date_bloom_ops</literal></entry>
+ <entry><type>date</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>date_minmax_ops</literal></entry>
<entry><type>date</type></entry>
@@ -227,6 +262,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>float8_bloom_ops</literal></entry>
+ <entry><type>double precision</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>float8_minmax_ops</literal></entry>
<entry><type>double precision</type></entry>
@@ -238,6 +280,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>inet_bloom_ops</literal></entry>
+ <entry><type>inet</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>inet_minmax_ops</literal></entry>
<entry><type>inet</type></entry>
@@ -261,6 +310,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal><<</literal>
</entry>
</row>
+ <row>
+ <entry><literal>int4_bloom_ops</literal></entry>
+ <entry><type>integer</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>int4_minmax_ops</literal></entry>
<entry><type>integer</type></entry>
@@ -272,6 +328,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>interval_bloom_ops</literal></entry>
+ <entry><type>interval</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>interval_minmax_ops</literal></entry>
<entry><type>interval</type></entry>
@@ -283,6 +346,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>macaddr_bloom_ops</literal></entry>
+ <entry><type>macaddr</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>macaddr_minmax_ops</literal></entry>
<entry><type>macaddr</type></entry>
@@ -294,6 +364,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>macaddr8_bloom_ops</literal></entry>
+ <entry><type>macaddr8</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>macaddr8_minmax_ops</literal></entry>
<entry><type>macaddr8</type></entry>
@@ -305,6 +382,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>name_bloom_ops</literal></entry>
+ <entry><type>name</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>name_minmax_ops</literal></entry>
<entry><type>name</type></entry>
@@ -316,6 +400,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>numeric_bloom_ops</literal></entry>
+ <entry><type>numeric</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>numeric_minmax_ops</literal></entry>
<entry><type>numeric</type></entry>
@@ -327,6 +418,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>pg_lsn_bloom_ops</literal></entry>
+ <entry><type>pg_lsn</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>pg_lsn_minmax_ops</literal></entry>
<entry><type>pg_lsn</type></entry>
@@ -338,6 +436,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>oid_bloom_ops</literal></entry>
+ <entry><type>oid</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>oid_minmax_ops</literal></entry>
<entry><type>oid</type></entry>
@@ -369,6 +474,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>>=</literal>
</entry>
</row>
+ <row>
+ <entry><literal>float4_bloom_ops</literal></entry>
+ <entry><type>real</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>float4_minmax_ops</literal></entry>
<entry><type>real</type></entry>
@@ -380,6 +492,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>int2_bloom_ops</literal></entry>
+ <entry><type>smallint</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>int2_minmax_ops</literal></entry>
<entry><type>smallint</type></entry>
@@ -391,6 +510,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>text_bloom_ops</literal></entry>
+ <entry><type>text</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>text_minmax_ops</literal></entry>
<entry><type>text</type></entry>
@@ -413,6 +539,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timestamp_bloom_ops</literal></entry>
+ <entry><type>timestamp without time zone</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>timestamp_minmax_ops</literal></entry>
<entry><type>timestamp without time zone</type></entry>
@@ -424,6 +557,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timestamptz_bloom_ops</literal></entry>
+ <entry><type>timestamp with time zone</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>timestamptz_minmax_ops</literal></entry>
<entry><type>timestamp with time zone</type></entry>
@@ -435,6 +575,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>time_bloom_ops</literal></entry>
+ <entry><type>time without time zone</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>time_minmax_ops</literal></entry>
<entry><type>time without time zone</type></entry>
@@ -446,6 +593,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timetz_bloom_ops</literal></entry>
+ <entry><type>time with time zone</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>timetz_minmax_ops</literal></entry>
<entry><type>time with time zone</type></entry>
@@ -457,6 +611,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>uuid_bloom_ops</literal></entry>
+ <entry><type>uuid</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>uuid_minmax_ops</literal></entry>
<entry><type>uuid</type></entry>
@@ -841,6 +1002,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..b119e4e264
--- /dev/null
+++ b/src/backend/access/brin/brin_bloom.c
@@ -0,0 +1,980 @@
+/*
+ * 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,
+ nvalues;
+ Size len;
+ uint32 *values;
+
+ /* never call compact on filters in HASH phase */
+ Assert(BLOOM_IS_SORTED(filter));
+
+ /* no chance to compact anything */
+ if (filter->nvalues == filter->nsorted)
+ return;
+
+ values = (uint32 *) filter->data;
+
+ /* TODO optimization: sort only the unsorted part, then merge */
+ pg_qsort(values, filter->nvalues, sizeof(uint32), cmp_uint32);
+
+ nvalues = 1;
+ for (i = 1; i < filter->nvalues; i++)
+ {
+ /* if different from the last value, keep it */
+ if (values[i] != values[nvalues - 1])
+ values[nvalues++] = values[i];
+ }
+
+ 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(BYTEAOID, 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();
+}
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..af6891e348 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)',
@@ -2002,6 +2065,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 +2096,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 +2118,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 +2140,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 +2182,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 +2204,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 +2355,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 +2410,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 +2432,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 +2488,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 +2510,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 +2576,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..c24a80545a 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' },
@@ -984,6 +1125,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 +1178,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 +1212,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 +1245,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 +1297,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 +1330,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 +1455,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 +1525,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 +1559,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 +1619,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 +1652,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 +1705,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..16d6111ab6 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -257,67 +257,127 @@
{ 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 => '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 +386,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..7f733aeab1 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -180,50 +180,86 @@
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 => '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 46cedaeee6..5e0d29b12d 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8099,6 +8099,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',
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 27056d70d3..729d5c04c1 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2009,6 +2009,7 @@ ORDER BY 1, 2, 3;
2742 | 16 | @@
3580 | 1 | <
3580 | 1 | <<
+ 3580 | 1 | =
3580 | 2 | &<
3580 | 2 | <=
3580 | 3 | &&
@@ -2072,7 +2073,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 7d2d6328fc..97f0c47005 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4929,8 +4929,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/parallel_schedule b/src/test/regress/parallel_schedule
index 026ea880cd..74db99976a 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 ba0c3abbbc456ebcc4187c7a8fdfd0fc6e0236c5 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Fri, 3 Jul 2020 02:14:59 +0200
Subject: [PATCH 5/5] BRIN multi-range minmax indexes
---
doc/src/sgml/brin.sgml | 212 +-
doc/src/sgml/ref/create_index.sgml | 11 +
src/backend/access/brin/Makefile | 1 +
src/backend/access/brin/brin_minmax_multi.c | 2178 +++++++++++++++++++
src/include/access/brin.h | 1 +
src/include/access/brin_internal.h | 3 +
src/include/access/transam.h | 8 +-
src/include/catalog/pg_amop.dat | 545 +++++
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 | 65 +
src/test/regress/expected/brin_multi.out | 421 ++++
src/test/regress/expected/psql.out | 13 +-
src/test/regress/parallel_schedule | 2 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/brin_multi.sql | 371 ++++
17 files changed, 4498 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 cc3533ef1f..1bf2e0dce7 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -262,6 +262,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>date_minmax_multi_ops</literal></entry>
+ <entry><type>date</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>float8_bloom_ops</literal></entry>
<entry><type>double precision</type></entry>
@@ -280,6 +291,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>float8_minmax_multi_ops</literal></entry>
+ <entry><type>double precision</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>inet_bloom_ops</literal></entry>
<entry><type>inet</type></entry>
@@ -298,6 +320,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>inet_minmax_multi_ops</literal></entry>
+ <entry><type>inet</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>network_inclusion_ops</literal></entry>
<entry><type>inet</type></entry>
@@ -346,6 +379,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>interval_minmax_multi_ops</literal></entry>
+ <entry><type>interval</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>macaddr_bloom_ops</literal></entry>
<entry><type>macaddr</type></entry>
@@ -364,6 +408,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>macaddr_minmax_multi_ops</literal></entry>
+ <entry><type>macaddr</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>macaddr8_bloom_ops</literal></entry>
<entry><type>macaddr8</type></entry>
@@ -382,6 +437,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>macaddr8_minmax_multi_ops</literal></entry>
+ <entry><type>macaddr8</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>name_bloom_ops</literal></entry>
<entry><type>name</type></entry>
@@ -436,6 +502,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>pg_lsn_minmax_multi_ops</literal></entry>
+ <entry><type>pg_lsn</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>oid_bloom_ops</literal></entry>
<entry><type>oid</type></entry>
@@ -557,6 +634,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timestamp_minmax_multi_ops</literal></entry>
+ <entry><type>timestamp without time zone</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>timestamptz_bloom_ops</literal></entry>
<entry><type>timestamp with time zone</type></entry>
@@ -575,6 +663,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timestamptz_minmax_multi_ops</literal></entry>
+ <entry><type>timestamp with time zone</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>time_bloom_ops</literal></entry>
<entry><type>time without time zone</type></entry>
@@ -593,6 +692,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>time_minmax_multi_ops</literal></entry>
+ <entry><type>time without time zone</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>timetz_bloom_ops</literal></entry>
<entry><type>time with time zone</type></entry>
@@ -611,6 +721,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timetz_minmax_multi_ops</literal></entry>
+ <entry><type>time with time zone</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>uuid_bloom_ops</literal></entry>
<entry><type>uuid</type></entry>
@@ -629,6 +750,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>uuid_minmax_multi_ops</literal></entry>
+ <entry><type>uuid</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -753,13 +885,13 @@ 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>
@@ -830,6 +962,72 @@ typedef struct BrinOpcInfo
</tgroup>
</table>
+ <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>
To write an operator class for a complex data type which has values
included within another type, it's possible to use the inclusion support
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 9c90d451a7..f6b8fbd1d6 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 64.
+ </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..9b9eedbb63
--- /dev/null
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -0,0 +1,2178 @@
+/*
+ * 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/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
+{
+ /* (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_;
+
+ /* (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,
+ AttrNumber attno, Form_pg_attribute attr);
+
+static Ranges *range_deserialize(SerializedRanges *range,
+ AttrNumber attno, Form_pg_attribute attr);
+
+/* 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, AttrNumber attno, Form_pg_attribute attr)
+{
+ Size len;
+ int nvalues;
+ SerializedRanges *serialized;
+
+ 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);
+
+ /* 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 (attr->attlen == -1) /* varlena */
+ {
+ int i;
+ for (i = 0; i < nvalues; i++)
+ {
+ len += VARSIZE_ANY(range->values[i]);
+ }
+ }
+ else if (attr->attlen == -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(attr->attlen > 0);
+ len += nvalues * attr->attlen;
+ }
+
+ /*
+ * 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->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 (attr->attbyval) /* simple by-value data types */
+ {
+ memcpy(ptr, &range->values[i], attr->attlen);
+ ptr += attr->attlen;
+ }
+ else if (attr->attlen > 0) /* fixed-length by-ref types */
+ {
+ memcpy(ptr, DatumGetPointer(range->values[i]), attr->attlen);
+ ptr += attr->attlen;
+ }
+ else if (attr->attlen == -1) /* varlena */
+ {
+ int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i]));
+ memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
+ ptr += tmp;
+ }
+ else if (attr->attlen == -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,
+ AttrNumber attno, Form_pg_attribute attr)
+{
+ int i,
+ nvalues;
+ char *ptr;
+
+ 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;
+
+ /*
+ * 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 (attr->attbyval) /* simple by-value data types */
+ {
+ memcpy(&range->values[i], ptr, attr->attlen);
+ ptr += attr->attlen;
+ }
+ else if (attr->attlen > 0) /* fixed-length by-ref types */
+ {
+ /* no copy, just set the value to the pointer */
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += attr->attlen;
+ }
+ else if (attr->attlen == -1) /* varlena */
+ {
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += VARSIZE_ANY(DatumGetPointer(range->values[i]));
+ }
+ else if (attr->attlen == -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 *cmpFn;
+ 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.
+ */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTLessStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, 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)
+ {
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTGreaterStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, 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.
+ */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTLessStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue);
+
+ /* smaller than the smallest value in this range */
+ if (DatumGetBool(compar))
+ continue;
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTGreaterStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue);
+
+ /* larger than the largest value in this range */
+ if (DatumGetBool(compar))
+ continue;
+
+ /* hey, we found a matching row */
+ return true;
+ }
+ }
+
+ /*
+ * 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;
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTEqualStrategyNumber);
+
+ compar = FunctionCall2Coll(cmpFn, 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);
+
+ /*
+ * 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_values(cranges, ncranges) <= 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));
+
+ /*
+ * 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;
+
+ /*
+ * 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)
+ {
+ FmgrInfo *cmpFn;
+ Datum *values;
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /* 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.
+ */
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /* 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(BYTEAOID, 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 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);
+
+ /*
+ * 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)
+ {
+ ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ column->bv_allnulls = false;
+ modified = true;
+ }
+ else
+ {
+ serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+ ranges = range_deserialize(serialized, attno, attr);
+ }
+
+ /*
+ * 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);
+
+ if (modified)
+ {
+ SerializedRanges *s = range_serialize(ranges, attno, attr);
+ column->bv_values[0] = PointerGetDatum(s);
+
+ /*
+ * XXX pfree must happen after range_serialize, because the Ranges value
+ * may reference the original serialized value.
+ */
+ if (serialized)
+ pfree(serialized);
+ }
+
+ pfree(ranges);
+
+ 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;
+ Form_pg_attribute attr;
+
+ attno = column->bv_attno;
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+
+ serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+ ranges = range_deserialize(serialized, attno, attr);
+
+ /* 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, attno, attr);
+ ranges_b = range_deserialize(serialized_b, attno, attr);
+
+ /* 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, attno, attr));
+
+ 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();
+}
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/transam.h b/src/include/access/transam.h
index a91a0c7487..18a8fd77d9 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -130,14 +130,14 @@ FullTransactionIdAdvance(FullTransactionId *dest)
* when the .dat files in src/include/catalog/ do not specify an OID
* for a catalog entry that requires one.
*
- * OIDS 12000-16383 are reserved for assignment during initdb
- * using the OID generator. (We start the generator at 12000.)
+ * OIDS 13000-16383 are reserved for assignment during initdb
+ * using the OID generator. (We start the generator at 13000.)
*
* OIDs beginning at 16384 are assigned from the OID generator
* during normal multiuser operation. (We force the generator up to
* 16384 as soon as we are in normal operation.)
*
- * The choices of 8000, 10000 and 12000 are completely arbitrary, and can be
+ * The choices of 8000, 10000 and 13000 are completely arbitrary, and can be
* moved if we run low on OIDs in any category. Changing the macros below,
* and updating relevant documentation (see bki.sgml and RELEASE_CHANGES),
* should be sufficient to do this. Moving the 16384 boundary between
@@ -151,7 +151,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 af6891e348..31ec56766a 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)',
@@ -1999,6 +2162,23 @@
amoprighttype => 'tid', amopstrategy => '5', 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)
{ amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4',
@@ -2065,6 +2245,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)',
@@ -2096,6 +2342,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',
@@ -2118,6 +2381,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',
@@ -2140,6 +2420,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)',
@@ -2204,6 +2501,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)',
@@ -2355,6 +2669,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',
@@ -2410,6 +2870,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',
@@ -2432,6 +2909,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)',
@@ -2488,6 +2982,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',
@@ -2510,6 +3021,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)',
@@ -2576,6 +3104,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 c24a80545a..317c9475f8 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' },
@@ -1075,6 +1238,23 @@
{ amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid',
amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' },
+# 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',
@@ -1125,6 +1305,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',
@@ -1178,6 +1432,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',
@@ -1212,6 +1486,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',
@@ -1245,6 +1539,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',
@@ -1330,6 +1644,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',
@@ -1455,6 +1788,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',
@@ -1525,6 +2022,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',
@@ -1559,6 +2076,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',
@@ -1619,6 +2156,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',
@@ -1650,7 +2207,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',
@@ -1705,6 +2283,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 16d6111ab6..92b3a577ea 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,38 +307,59 @@
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' },
{ opcmethod => 'brin', opcname => 'tid_minmax_ops',
opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' },
+{ 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' },
@@ -345,36 +375,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' },
@@ -386,6 +434,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' },
@@ -395,6 +446,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' },
@@ -404,6 +458,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 7f733aeab1..8bbf753f65 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,24 +220,36 @@
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 => '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',
@@ -240,10 +260,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',
@@ -252,12 +276,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 5e0d29b12d..fa324f4608 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8082,6 +8082,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',
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 97f0c47005..f18097a02b 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4926,12 +4926,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/parallel_schedule b/src/test/regress/parallel_schedule
index 74db99976a..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
Attachments:
[text/plain] 0001-Pass-all-keys-to-BRIN-consistent-function-a-20200703.patch (20.8K, ../20200703005737.dw5i2uwd6i3gv5lf@development/2-0001-Pass-all-keys-to-BRIN-consistent-function-a-20200703.patch)
download | inline diff:
From ac8e74149e8ce6c291e8c414872e1fb2903836d0 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Fri, 13 Sep 2019 18:34:39 +0200
Subject: [PATCH 1/5] Pass all keys to BRIN consistent function at once
---
src/backend/access/brin/brin.c | 126 ++++++++++++-----
src/backend/access/brin/brin_inclusion.c | 164 +++++++++++++++--------
src/backend/access/brin/brin_minmax.c | 116 +++++++++++-----
src/backend/access/brin/brin_validate.c | 4 +-
src/include/catalog/pg_proc.dat | 4 +-
5 files changed, 293 insertions(+), 121 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 7db3ae5ee0..f1beb2eff9 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -388,6 +388,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;
@@ -409,6 +412,53 @@ 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;
+
+ 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);
@@ -469,7 +519,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
else
{
- int keyno;
+ int attno;
/*
* Compare scan keys with summary values stored for the range.
@@ -479,34 +529,19 @@ 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])
+ 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
@@ -518,12 +553,43 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
* the range as a whole, so break out of the loop as soon
* as a false return value is obtained.
*/
- 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 */
+ 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..8968886ff5 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,103 @@ 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 matches;
+ 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);
}
-
- /*
- * 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);
-
- /*
- * 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];
+ matches = true;
+
+ 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;
+
+ matches = inclusion_consistent_key(bdesc, column, key, colloid);
+
+ if (!matches)
+ break;
+ }
+
+ PG_RETURN_BOOL(matches);
+}
+
+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 +376,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 +437,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 +458,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 +477,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 +512,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 +522,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..1219a3a2ab 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,99 @@ 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;
+ ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2);
+ int nkeys = PG_GETARG_INT32(3);
+ Oid colloid = PG_GET_COLLATION();
Datum matches;
- FmgrInfo *finfo;
+ int keyno;
+ bool regular_keys = false;
- Assert(key->sk_attno == column->bv_attno);
-
- /* 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);
-
- /*
- * Neither IS NULL nor IS NOT NULL was used; assume all indexable
- * operators are strict and return false.
- */
+ /*
+ * 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);
+
+ matches = true;
+
+ 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;
+
+ matches = minmax_consistent_key(bdesc, column, key, colloid);
+
+ /* found non-matching key */
+ if (!matches)
+ break;
}
- /* if the range is all empty, it cannot possibly be consistent */
- if (column->bv_allnulls)
- PG_RETURN_BOOL(false);
+ PG_RETURN_DATUM(matches);
+}
+
+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 +283,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 38295aca48..46cedaeee6 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8076,7 +8076,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',
@@ -8092,7 +8092,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-checks-to-bringetbitmap-20200703.patch (9.9K, ../20200703005737.dw5i2uwd6i3gv5lf@development/3-0002-Move-IS-NOT-NULL-checks-to-bringetbitmap-20200703.patch)
download | inline diff:
From 1f175a68c2d5332bb86d62036bc8771b233b8791 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 9 Jun 2019 22:05:39 +0200
Subject: [PATCH 2/5] Move IS NOT NULL checks to bringetbitmap
---
src/backend/access/brin/brin.c | 116 ++++++++++++++++++++---
src/backend/access/brin/brin_inclusion.c | 62 +-----------
src/backend/access/brin/brin_minmax.c | 62 +-----------
3 files changed, 109 insertions(+), 131 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index f1beb2eff9..25ae02a999 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -388,8 +388,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;
@@ -414,10 +416,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,14 +444,12 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
keyattno - 1)->attcollation));
/* First time we see this index attribute, so init as needed. */
- if (!keys[keyattno-1])
+ if (consistentFn[keyattno - 1].fn_oid == InvalidOid)
{
FmgrInfo *tmp;
- keys[keyattno-1] = palloc0(sizeof(ScanKey) * scan->numberOfKeys);
-
- /* First time this column, so look up consistent function */
- Assert(consistentFn[keyattno - 1].fn_oid == InvalidOid);
+ 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);
@@ -454,9 +457,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 */
@@ -543,6 +560,83 @@ 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.
+ */
+ for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++)
+ {
+ ScanKey key = nullkeys[attno - 1][keyno];
+
+ Assert(key->sk_attno == bval->bv_attno);
+
+ /* interrupt the loop as soon as we find a mismatch */
+ if (!addrange)
+ break;
+
+ /* handle IS NULL/IS NOT NULL tests */
+ if (key->sk_flags & SK_ISNULL)
+ {
+ /* IS NULL scan key, but range has no NULLs */
+ if (key->sk_flags & SK_SEARCHNULL)
+ {
+ if (!bval->bv_allnulls && !bval->bv_hasnulls)
+ addrange = false;
+
+ continue;
+ }
+
+ /*
+ * For IS NOT NULL, we can only skip ranges that are
+ * known to have only nulls.
+ */
+ if (key->sk_flags & SK_SEARCHNOTNULL)
+ {
+ if (bval->bv_allnulls)
+ addrange = false;
+
+ continue;
+ }
+
+ /*
+ * 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.
+ */
+ addrange = false;
+ }
+ }
+
+ /*
+ * If any of the IS [NOT] NULL keys failed, the page range as
+ * a whole can't pass. So terminate the loop.
+ */
+ if (!addrange)
+ 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
diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c
index 8968886ff5..22edc6b46f 100644
--- a/src/backend/access/brin/brin_inclusion.c
+++ b/src/backend/access/brin/brin_inclusion.c
@@ -265,63 +265,6 @@ brin_inclusion_consistent(PG_FUNCTION_ARGS)
Oid colloid = PG_GET_COLLATION();
int keyno;
bool matches;
- 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]))
@@ -333,9 +276,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));
matches = inclusion_consistent_key(bdesc, column, key, colloid);
diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c
index 1219a3a2ab..7a7bd21cec 100644
--- a/src/backend/access/brin/brin_minmax.c
+++ b/src/backend/access/brin/brin_minmax.c
@@ -153,63 +153,6 @@ brin_minmax_consistent(PG_FUNCTION_ARGS)
Oid colloid = PG_GET_COLLATION();
Datum matches;
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);
matches = true;
@@ -217,9 +160,8 @@ brin_minmax_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));
matches = minmax_consistent_key(bdesc, column, key, colloid);
--
2.25.4
[text/plain] 0003-Move-processing-of-NULLs-from-BRIN-support--20200703.patch (16.1K, ../20200703005737.dw5i2uwd6i3gv5lf@development/4-0003-Move-processing-of-NULLs-from-BRIN-support--20200703.patch)
download | inline diff:
From f77e38ab6997775a75d0ca03fd8c2ced094dceeb Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tv@fuzzy.cz>
Date: Thu, 2 Apr 2020 02:56:00 +0200
Subject: [PATCH 3/5] Move processing of NULLs from BRIN support functions
---
src/backend/access/brin/brin.c | 260 ++++++++++++++---------
src/backend/access/brin/brin_inclusion.c | 44 +---
src/backend/access/brin/brin_minmax.c | 41 +---
src/include/access/brin_internal.h | 3 +
4 files changed, 169 insertions(+), 179 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 25ae02a999..8c94252567 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
@@ -177,7 +180,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
OffsetNumber off;
BrinTuple *brtup;
BrinMemTuple *dtup;
- int keyno;
CHECK_FOR_INTERRUPTS();
@@ -241,31 +243,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)
{
@@ -358,6 +336,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys)
return scan;
}
+
/*
* Execute the index scan.
*
@@ -561,69 +540,31 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
(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.
+ * 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.
*/
- for (keyno = 0; (keyno < nnullkeys[attno - 1]); keyno++)
+ if (bdesc->bd_info[attno - 1]->oi_regular_nulls &&
+ !check_null_keys(bval, nullkeys[attno - 1],
+ nnullkeys[attno - 1]))
{
- ScanKey key = nullkeys[attno - 1][keyno];
-
- Assert(key->sk_attno == bval->bv_attno);
-
- /* interrupt the loop as soon as we find a mismatch */
- if (!addrange)
- break;
-
- /* handle IS NULL/IS NOT NULL tests */
- if (key->sk_flags & SK_ISNULL)
- {
- /* IS NULL scan key, but range has no NULLs */
- if (key->sk_flags & SK_SEARCHNULL)
- {
- if (!bval->bv_allnulls && !bval->bv_hasnulls)
- addrange = false;
-
- continue;
- }
-
- /*
- * For IS NOT NULL, we can only skip ranges that are
- * known to have only nulls.
- */
- if (key->sk_flags & SK_SEARCHNOTNULL)
- {
- if (bval->bv_allnulls)
- addrange = false;
-
- continue;
- }
-
- /*
- * 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.
- */
- addrange = false;
- }
- }
-
- /*
- * If any of the IS [NOT] NULL keys failed, the page range as
- * a whole can't pass. So terminate the loop.
- */
- if (!addrange)
+ /*
+ * 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).
+ * 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. */
@@ -771,7 +712,6 @@ brinbuildCallback(Relation index,
{
BrinBuildState *state = (BrinBuildState *) brstate;
BlockNumber thisblock;
- int i;
thisblock = ItemPointerGetBlockNumber(tid);
@@ -800,25 +740,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);
}
/*
@@ -1597,6 +1520,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);
@@ -1650,3 +1606,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 22edc6b46f..59503b6f68 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);
@@ -510,37 +500,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 7a7bd21cec..8882eec12c 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);
@@ -245,34 +235,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] 0004-BRIN-bloom-indexes-20200703.patch (126.7K, ../20200703005737.dw5i2uwd6i3gv5lf@development/5-0004-BRIN-bloom-indexes-20200703.patch)
download | inline diff:
From 9d01bf0157c545bbad5699b38afcaf6adbb820a5 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tv@fuzzy.cz>
Date: Thu, 2 Apr 2020 02:57:59 +0200
Subject: [PATCH 4/5] BRIN bloom indexes
---
doc/src/sgml/brin.sgml | 215 +++++
doc/src/sgml/ref/create_index.sgml | 31 +
src/backend/access/brin/Makefile | 1 +
src/backend/access/brin/brin_bloom.c | 980 +++++++++++++++++++++++
src/include/access/brin.h | 2 +
src/include/access/brin_internal.h | 4 +
src/include/catalog/pg_amop.dat | 165 ++++
src/include/catalog/pg_amproc.dat | 430 ++++++++++
src/include/catalog/pg_opclass.dat | 69 ++
src/include/catalog/pg_opfamily.dat | 36 +
src/include/catalog/pg_proc.dat | 20 +
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/parallel_schedule | 5 +
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/brin_bloom.sql | 404 ++++++++++
17 files changed, 2823 insertions(+), 2 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 4c5eeb875f..cc3533ef1f 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -132,6 +132,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
</row>
</thead>
<tbody>
+ <row>
+ <entry><literal>int8_bloom_ops</literal></entry>
+ <entry><type>bigint</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>int8_minmax_ops</literal></entry>
<entry><type>bigint</type></entry>
@@ -183,6 +190,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>|&></literal>
</entry>
</row>
+ <row>
+ <entry><literal>bytea_bloom_ops</literal></entry>
+ <entry><type>bytea</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>bytea_minmax_ops</literal></entry>
<entry><type>bytea</type></entry>
@@ -194,6 +208,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>bpchar_bloom_ops</literal></entry>
+ <entry><type>character</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>bpchar_minmax_ops</literal></entry>
<entry><type>character</type></entry>
@@ -205,6 +226,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>char_bloom_ops</literal></entry>
+ <entry><type>"char"</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>char_minmax_ops</literal></entry>
<entry><type>"char"</type></entry>
@@ -216,6 +244,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>date_bloom_ops</literal></entry>
+ <entry><type>date</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>date_minmax_ops</literal></entry>
<entry><type>date</type></entry>
@@ -227,6 +262,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>float8_bloom_ops</literal></entry>
+ <entry><type>double precision</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>float8_minmax_ops</literal></entry>
<entry><type>double precision</type></entry>
@@ -238,6 +280,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>inet_bloom_ops</literal></entry>
+ <entry><type>inet</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>inet_minmax_ops</literal></entry>
<entry><type>inet</type></entry>
@@ -261,6 +310,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal><<</literal>
</entry>
</row>
+ <row>
+ <entry><literal>int4_bloom_ops</literal></entry>
+ <entry><type>integer</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>int4_minmax_ops</literal></entry>
<entry><type>integer</type></entry>
@@ -272,6 +328,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>interval_bloom_ops</literal></entry>
+ <entry><type>interval</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>interval_minmax_ops</literal></entry>
<entry><type>interval</type></entry>
@@ -283,6 +346,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>macaddr_bloom_ops</literal></entry>
+ <entry><type>macaddr</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>macaddr_minmax_ops</literal></entry>
<entry><type>macaddr</type></entry>
@@ -294,6 +364,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>macaddr8_bloom_ops</literal></entry>
+ <entry><type>macaddr8</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>macaddr8_minmax_ops</literal></entry>
<entry><type>macaddr8</type></entry>
@@ -305,6 +382,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>name_bloom_ops</literal></entry>
+ <entry><type>name</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>name_minmax_ops</literal></entry>
<entry><type>name</type></entry>
@@ -316,6 +400,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>numeric_bloom_ops</literal></entry>
+ <entry><type>numeric</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>numeric_minmax_ops</literal></entry>
<entry><type>numeric</type></entry>
@@ -327,6 +418,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>pg_lsn_bloom_ops</literal></entry>
+ <entry><type>pg_lsn</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>pg_lsn_minmax_ops</literal></entry>
<entry><type>pg_lsn</type></entry>
@@ -338,6 +436,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>oid_bloom_ops</literal></entry>
+ <entry><type>oid</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>oid_minmax_ops</literal></entry>
<entry><type>oid</type></entry>
@@ -369,6 +474,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>>=</literal>
</entry>
</row>
+ <row>
+ <entry><literal>float4_bloom_ops</literal></entry>
+ <entry><type>real</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>float4_minmax_ops</literal></entry>
<entry><type>real</type></entry>
@@ -380,6 +492,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>int2_bloom_ops</literal></entry>
+ <entry><type>smallint</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>int2_minmax_ops</literal></entry>
<entry><type>smallint</type></entry>
@@ -391,6 +510,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>text_bloom_ops</literal></entry>
+ <entry><type>text</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>text_minmax_ops</literal></entry>
<entry><type>text</type></entry>
@@ -413,6 +539,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timestamp_bloom_ops</literal></entry>
+ <entry><type>timestamp without time zone</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>timestamp_minmax_ops</literal></entry>
<entry><type>timestamp without time zone</type></entry>
@@ -424,6 +557,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timestamptz_bloom_ops</literal></entry>
+ <entry><type>timestamp with time zone</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>timestamptz_minmax_ops</literal></entry>
<entry><type>timestamp with time zone</type></entry>
@@ -435,6 +575,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>time_bloom_ops</literal></entry>
+ <entry><type>time without time zone</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>time_minmax_ops</literal></entry>
<entry><type>time without time zone</type></entry>
@@ -446,6 +593,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timetz_bloom_ops</literal></entry>
+ <entry><type>time with time zone</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>timetz_minmax_ops</literal></entry>
<entry><type>time with time zone</type></entry>
@@ -457,6 +611,13 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>uuid_bloom_ops</literal></entry>
+ <entry><type>uuid</type></entry>
+ <entry>
+ <literal>=</literal>
+ </entry>
+ </row>
<row>
<entry><literal>uuid_minmax_ops</literal></entry>
<entry><type>uuid</type></entry>
@@ -841,6 +1002,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..b119e4e264
--- /dev/null
+++ b/src/backend/access/brin/brin_bloom.c
@@ -0,0 +1,980 @@
+/*
+ * 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,
+ nvalues;
+ Size len;
+ uint32 *values;
+
+ /* never call compact on filters in HASH phase */
+ Assert(BLOOM_IS_SORTED(filter));
+
+ /* no chance to compact anything */
+ if (filter->nvalues == filter->nsorted)
+ return;
+
+ values = (uint32 *) filter->data;
+
+ /* TODO optimization: sort only the unsorted part, then merge */
+ pg_qsort(values, filter->nvalues, sizeof(uint32), cmp_uint32);
+
+ nvalues = 1;
+ for (i = 1; i < filter->nvalues; i++)
+ {
+ /* if different from the last value, keep it */
+ if (values[i] != values[nvalues - 1])
+ values[nvalues++] = values[i];
+ }
+
+ 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(BYTEAOID, 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();
+}
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..af6891e348 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)',
@@ -2002,6 +2065,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 +2096,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 +2118,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 +2140,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 +2182,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 +2204,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 +2355,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 +2410,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 +2432,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 +2488,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 +2510,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 +2576,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..c24a80545a 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' },
@@ -984,6 +1125,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 +1178,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 +1212,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 +1245,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 +1297,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 +1330,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 +1455,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 +1525,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 +1559,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 +1619,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 +1652,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 +1705,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..16d6111ab6 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -257,67 +257,127 @@
{ 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 => '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 +386,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..7f733aeab1 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -180,50 +180,86 @@
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 => '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 46cedaeee6..5e0d29b12d 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8099,6 +8099,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',
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 27056d70d3..729d5c04c1 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2009,6 +2009,7 @@ ORDER BY 1, 2, 3;
2742 | 16 | @@
3580 | 1 | <
3580 | 1 | <<
+ 3580 | 1 | =
3580 | 2 | &<
3580 | 2 | <=
3580 | 3 | &&
@@ -2072,7 +2073,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 7d2d6328fc..97f0c47005 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4929,8 +4929,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/parallel_schedule b/src/test/regress/parallel_schedule
index 026ea880cd..74db99976a 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] 0005-BRIN-multi-range-minmax-indexes-20200703.patch (198.0K, ../20200703005737.dw5i2uwd6i3gv5lf@development/6-0005-BRIN-multi-range-minmax-indexes-20200703.patch)
download | inline diff:
From ba0c3abbbc456ebcc4187c7a8fdfd0fc6e0236c5 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Fri, 3 Jul 2020 02:14:59 +0200
Subject: [PATCH 5/5] BRIN multi-range minmax indexes
---
doc/src/sgml/brin.sgml | 212 +-
doc/src/sgml/ref/create_index.sgml | 11 +
src/backend/access/brin/Makefile | 1 +
src/backend/access/brin/brin_minmax_multi.c | 2178 +++++++++++++++++++
src/include/access/brin.h | 1 +
src/include/access/brin_internal.h | 3 +
src/include/access/transam.h | 8 +-
src/include/catalog/pg_amop.dat | 545 +++++
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 | 65 +
src/test/regress/expected/brin_multi.out | 421 ++++
src/test/regress/expected/psql.out | 13 +-
src/test/regress/parallel_schedule | 2 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/brin_multi.sql | 371 ++++
17 files changed, 4498 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 cc3533ef1f..1bf2e0dce7 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -262,6 +262,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>date_minmax_multi_ops</literal></entry>
+ <entry><type>date</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>float8_bloom_ops</literal></entry>
<entry><type>double precision</type></entry>
@@ -280,6 +291,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>float8_minmax_multi_ops</literal></entry>
+ <entry><type>double precision</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>inet_bloom_ops</literal></entry>
<entry><type>inet</type></entry>
@@ -298,6 +320,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>inet_minmax_multi_ops</literal></entry>
+ <entry><type>inet</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>network_inclusion_ops</literal></entry>
<entry><type>inet</type></entry>
@@ -346,6 +379,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>interval_minmax_multi_ops</literal></entry>
+ <entry><type>interval</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>macaddr_bloom_ops</literal></entry>
<entry><type>macaddr</type></entry>
@@ -364,6 +408,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>macaddr_minmax_multi_ops</literal></entry>
+ <entry><type>macaddr</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>macaddr8_bloom_ops</literal></entry>
<entry><type>macaddr8</type></entry>
@@ -382,6 +437,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>macaddr8_minmax_multi_ops</literal></entry>
+ <entry><type>macaddr8</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>name_bloom_ops</literal></entry>
<entry><type>name</type></entry>
@@ -436,6 +502,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>pg_lsn_minmax_multi_ops</literal></entry>
+ <entry><type>pg_lsn</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>oid_bloom_ops</literal></entry>
<entry><type>oid</type></entry>
@@ -557,6 +634,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timestamp_minmax_multi_ops</literal></entry>
+ <entry><type>timestamp without time zone</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>timestamptz_bloom_ops</literal></entry>
<entry><type>timestamp with time zone</type></entry>
@@ -575,6 +663,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timestamptz_minmax_multi_ops</literal></entry>
+ <entry><type>timestamp with time zone</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>time_bloom_ops</literal></entry>
<entry><type>time without time zone</type></entry>
@@ -593,6 +692,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>time_minmax_multi_ops</literal></entry>
+ <entry><type>time without time zone</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>timetz_bloom_ops</literal></entry>
<entry><type>time with time zone</type></entry>
@@ -611,6 +721,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>timetz_minmax_multi_ops</literal></entry>
+ <entry><type>time with time zone</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
<row>
<entry><literal>uuid_bloom_ops</literal></entry>
<entry><type>uuid</type></entry>
@@ -629,6 +750,17 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<literal>></literal>
</entry>
</row>
+ <row>
+ <entry><literal>uuid_minmax_multi_ops</literal></entry>
+ <entry><type>uuid</type></entry>
+ <entry>
+ <literal><</literal>
+ <literal><=</literal>
+ <literal>=</literal>
+ <literal>>=</literal>
+ <literal>></literal>
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -753,13 +885,13 @@ 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>
@@ -830,6 +962,72 @@ typedef struct BrinOpcInfo
</tgroup>
</table>
+ <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>
To write an operator class for a complex data type which has values
included within another type, it's possible to use the inclusion support
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 9c90d451a7..f6b8fbd1d6 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 64.
+ </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..9b9eedbb63
--- /dev/null
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -0,0 +1,2178 @@
+/*
+ * 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/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
+{
+ /* (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_;
+
+ /* (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,
+ AttrNumber attno, Form_pg_attribute attr);
+
+static Ranges *range_deserialize(SerializedRanges *range,
+ AttrNumber attno, Form_pg_attribute attr);
+
+/* 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, AttrNumber attno, Form_pg_attribute attr)
+{
+ Size len;
+ int nvalues;
+ SerializedRanges *serialized;
+
+ 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);
+
+ /* 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 (attr->attlen == -1) /* varlena */
+ {
+ int i;
+ for (i = 0; i < nvalues; i++)
+ {
+ len += VARSIZE_ANY(range->values[i]);
+ }
+ }
+ else if (attr->attlen == -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(attr->attlen > 0);
+ len += nvalues * attr->attlen;
+ }
+
+ /*
+ * 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->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 (attr->attbyval) /* simple by-value data types */
+ {
+ memcpy(ptr, &range->values[i], attr->attlen);
+ ptr += attr->attlen;
+ }
+ else if (attr->attlen > 0) /* fixed-length by-ref types */
+ {
+ memcpy(ptr, DatumGetPointer(range->values[i]), attr->attlen);
+ ptr += attr->attlen;
+ }
+ else if (attr->attlen == -1) /* varlena */
+ {
+ int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i]));
+ memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
+ ptr += tmp;
+ }
+ else if (attr->attlen == -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,
+ AttrNumber attno, Form_pg_attribute attr)
+{
+ int i,
+ nvalues;
+ char *ptr;
+
+ 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;
+
+ /*
+ * 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 (attr->attbyval) /* simple by-value data types */
+ {
+ memcpy(&range->values[i], ptr, attr->attlen);
+ ptr += attr->attlen;
+ }
+ else if (attr->attlen > 0) /* fixed-length by-ref types */
+ {
+ /* no copy, just set the value to the pointer */
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += attr->attlen;
+ }
+ else if (attr->attlen == -1) /* varlena */
+ {
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += VARSIZE_ANY(DatumGetPointer(range->values[i]));
+ }
+ else if (attr->attlen == -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 *cmpFn;
+ 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.
+ */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTLessStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, 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)
+ {
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTGreaterStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, 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.
+ */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTLessStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, colloid, newval, minvalue);
+
+ /* smaller than the smallest value in this range */
+ if (DatumGetBool(compar))
+ continue;
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTGreaterStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, colloid, newval, maxvalue);
+
+ /* larger than the largest value in this range */
+ if (DatumGetBool(compar))
+ continue;
+
+ /* hey, we found a matching row */
+ return true;
+ }
+ }
+
+ /*
+ * 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;
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTEqualStrategyNumber);
+
+ compar = FunctionCall2Coll(cmpFn, 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);
+
+ /*
+ * 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_values(cranges, ncranges) <= 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));
+
+ /*
+ * 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;
+
+ /*
+ * 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)
+ {
+ FmgrInfo *cmpFn;
+ Datum *values;
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /* 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.
+ */
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /* 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(BYTEAOID, 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 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);
+
+ /*
+ * 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)
+ {
+ ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ column->bv_allnulls = false;
+ modified = true;
+ }
+ else
+ {
+ serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+ ranges = range_deserialize(serialized, attno, attr);
+ }
+
+ /*
+ * 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);
+
+ if (modified)
+ {
+ SerializedRanges *s = range_serialize(ranges, attno, attr);
+ column->bv_values[0] = PointerGetDatum(s);
+
+ /*
+ * XXX pfree must happen after range_serialize, because the Ranges value
+ * may reference the original serialized value.
+ */
+ if (serialized)
+ pfree(serialized);
+ }
+
+ pfree(ranges);
+
+ 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;
+ Form_pg_attribute attr;
+
+ attno = column->bv_attno;
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+
+ serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+ ranges = range_deserialize(serialized, attno, attr);
+
+ /* 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, attno, attr);
+ ranges_b = range_deserialize(serialized_b, attno, attr);
+
+ /* 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, attno, attr));
+
+ 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();
+}
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/transam.h b/src/include/access/transam.h
index a91a0c7487..18a8fd77d9 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -130,14 +130,14 @@ FullTransactionIdAdvance(FullTransactionId *dest)
* when the .dat files in src/include/catalog/ do not specify an OID
* for a catalog entry that requires one.
*
- * OIDS 12000-16383 are reserved for assignment during initdb
- * using the OID generator. (We start the generator at 12000.)
+ * OIDS 13000-16383 are reserved for assignment during initdb
+ * using the OID generator. (We start the generator at 13000.)
*
* OIDs beginning at 16384 are assigned from the OID generator
* during normal multiuser operation. (We force the generator up to
* 16384 as soon as we are in normal operation.)
*
- * The choices of 8000, 10000 and 12000 are completely arbitrary, and can be
+ * The choices of 8000, 10000 and 13000 are completely arbitrary, and can be
* moved if we run low on OIDs in any category. Changing the macros below,
* and updating relevant documentation (see bki.sgml and RELEASE_CHANGES),
* should be sufficient to do this. Moving the 16384 boundary between
@@ -151,7 +151,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 af6891e348..31ec56766a 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)',
@@ -1999,6 +2162,23 @@
amoprighttype => 'tid', amopstrategy => '5', 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)
{ amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4',
@@ -2065,6 +2245,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)',
@@ -2096,6 +2342,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',
@@ -2118,6 +2381,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',
@@ -2140,6 +2420,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)',
@@ -2204,6 +2501,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)',
@@ -2355,6 +2669,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',
@@ -2410,6 +2870,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',
@@ -2432,6 +2909,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)',
@@ -2488,6 +2982,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',
@@ -2510,6 +3021,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)',
@@ -2576,6 +3104,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 c24a80545a..317c9475f8 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' },
@@ -1075,6 +1238,23 @@
{ amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid',
amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' },
+# 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',
@@ -1125,6 +1305,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',
@@ -1178,6 +1432,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',
@@ -1212,6 +1486,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',
@@ -1245,6 +1539,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',
@@ -1330,6 +1644,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',
@@ -1455,6 +1788,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',
@@ -1525,6 +2022,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',
@@ -1559,6 +2076,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',
@@ -1619,6 +2156,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',
@@ -1650,7 +2207,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',
@@ -1705,6 +2283,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 16d6111ab6..92b3a577ea 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,38 +307,59 @@
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' },
{ opcmethod => 'brin', opcname => 'tid_minmax_ops',
opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' },
+{ 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' },
@@ -345,36 +375,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' },
@@ -386,6 +434,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' },
@@ -395,6 +446,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' },
@@ -404,6 +458,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 7f733aeab1..8bbf753f65 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,24 +220,36 @@
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 => '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',
@@ -240,10 +260,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',
@@ -252,12 +276,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 5e0d29b12d..fa324f4608 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8082,6 +8082,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',
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 97f0c47005..f18097a02b 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4926,12 +4926,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/parallel_schedule b/src/test/regress/parallel_schedule
index 74db99976a..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
view thread (177+ messages) latest in thread
Message-ID: <20200703005737.dw5i2uwd6i3gv5lf@development>
Permalink: ../20200703005737.dw5i2uwd6i3gv5lf@development/
Also on: postgresql.org/message-id/20200703005737.dw5i2uwd6i3gv5lf@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, a.korotkov@postgrespro.ru, michael@paquier.xyz, alvherre@2ndquadrant.com, tgl@sss.pgh.pa.us, andres@anarazel.de, hornschnorter@gmail.com
Subject: Re: WIP: BRIN multi-range indexes
In-Reply-To: <20200703005737.dw5i2uwd6i3gv5lf@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