pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Tomas Vondra <tomas.vondra@enterprisedb.com>
To: John Naylor <john.naylor@enterprisedb.com>
Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>
Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Subject: Re: WIP: BRIN multi-range indexes
Date: Tue, 23 Mar 2021 02:13:10 +0100
Message-ID: <357fb6d7-649f-822e-ff43-16b39aed68ee@enterprisedb.com> (raw)
In-Reply-To: <54b47e66-bd8a-d44a-2090-fd4b2f49abe6@enterprisedb.com>
References: <20210303001725.GA1766@alvherre.pgsql>
	<23a0a854-8f1a-e965-7121-f76b1b2fc271@enterprisedb.com>
	<cf8fca26-0fdd-ac74-5dac-66a2ccb59f56@enterprisedb.com>
	<8e319879-32ff-1eb8-d7d3-6a3ac8a51728@enterprisedb.com>
	<CAFBsxsHrqP+OMp5cV6yiEzkwobtAp4rnxOGsfm_s_vX8qSNQeQ@mail.gmail.com>
	<35e866e9-d820-07de-b2be-69d5ac479179@enterprisedb.com>
	<71dfd257-c5ce-530d-90d3-7c744a28e97b@enterprisedb.com>
	<CAFBsxsHswPBXQvvW87uSR59MkPNhEB5Txe4sFjVBfE9TR4jrGw@mail.gmail.com>
	<ea369ea5-0d61-bbee-c1c7-bfe17728bab1@enterprisedb.com>
	<54b47e66-bd8a-d44a-2090-fd4b2f49abe6@enterprisedb.com>

I've pushed the first couple patches, reworking the BRIN interface and
the two existing opclasses. Attached are are the remaining bits,
implementing the two new opclasses.

0001 fixes the opckeytype I explained in the previous message. I'll
start a new thread, so that it does not get buried in this thread.

The 0002 and 0003 are the "main" patches, implementing all the stuff as
in-core opclasses. These patches were reviewed multiple times before, no
new changes.

0004 and 0005 are the patches moving the new opclasses to contrib. This
should also undo the catversion and OID generation changes from 0002 and
0003, but I'll take care of that if we actually decide contrib is the
right way. I kinda like it, if we can solve the two issues:

1) the opckeytype - I think this is a bug elsewhere, affecting any
opclass created by CREATE OPERATOR CLASS and not by directly injecting
the data into catalogs. I'll start a separate thread for that.

2) the pageinspect - Without knowing OID of the types used for summary,
brin_page_items can't print info about the bloom filter, minmax-multi
ranges, etc. Unfortunately, by moving the code to contrib we lose the
static OID assignment. I think there are three solutions to this:

a) Just use BYTEAOID, and accept that pageinspect prints just and
incomprehensible stream of characters

b) Use TypenameGetTypidExtended() to lookup the type by name. This is
what the code does now, but I'm afraid this might have security issues
due to search_path shenanigans. Base types can be created only by
superusers, but it's possible to create a composite type, and confuse
the lookup ...

c) Don't bother passing OID, and instead pass a pointer to the output
function directly. We'd need to extend BrinOpcInfo a bit, but that seems
like the simplest solution.

I think (b) is too dangerous/fragile, (a) is the simplest and (c) is a
bit too invasive/ugly I think.


regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

  [text/x-patch] 0001-fixup-opclass-storage-type-20210323.patch (965B, ../357fb6d7-649f-822e-ff43-16b39aed68ee@enterprisedb.com/2-0001-fixup-opclass-storage-type-20210323.patch)
  download | inline diff:
From 4ee5e2bbd0c44c82d1604836db5352f3e0e0fbf2 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Mon, 22 Mar 2021 20:36:59 +0100
Subject: [PATCH 1/5] fixup opclass storage type

---
 src/backend/commands/opclasscmds.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index fad39e2b75..78b2d69782 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -576,10 +576,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	 */
 	if (OidIsValid(storageoid))
 	{
-		/* Just drop the spec if same as column datatype */
-		if (storageoid == typeoid)
-			storageoid = InvalidOid;
-		else if (!amstorage)
+		if ((storageoid != typeoid) && (!amstorage))
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					 errmsg("storage type cannot be different from data type for access method \"%s\"",
-- 
2.30.2

  [text/x-patch] 0002-BRIN-bloom-indexes-20210323.patch (128.2K, ../357fb6d7-649f-822e-ff43-16b39aed68ee@enterprisedb.com/3-0002-BRIN-bloom-indexes-20210323.patch)
  download | inline diff:
From 4dc43bac76cbcf8d9f4015faa6c56f607aaf63bb Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Tue, 23 Mar 2021 01:02:55 +0100
Subject: [PATCH 2/5] BRIN bloom indexes

Adds a BRIN opclass using a Bloom filter to summarize the range. BRIN
indexes using the new opclasses only allow equality queries, but that
works for data like UUID, MAC addresses etc. for which range queries are
not very common (and the data look random, making BRIN minmax indexes
inefficient anyway).

BRIN bloom opclasses allow specifying the usual Bloom parameters, like
expected number of distinct values (in the BRIN range) and desired false
positive rate.

The opclasses do not operate directly on the indexed values, but on
32-bit hashes of the values. That assumes the hash functions for data
types have a low number of collisions, good performance etc. Collisions
should not be a huge issue though, because the number of values in a
BRIN ranges is usually fairly small.

Bump catversion, due to various catalog changes.

Author: Tomas Vondra <tomas.vondra@postgresql.org>
Reviewed-by: Alvaro Herrera <alvherre@alvh.no-ip.org>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru>
Reviewed-by: Nico Williams <nico@cryptonector.com>
Reviewed-by: John Naylor <john.naylor@enterprisedb.com>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com
---
 doc/src/sgml/brin.sgml                    | 226 +++++-
 src/backend/access/brin/Makefile          |   1 +
 src/backend/access/brin/brin_bloom.c      | 809 ++++++++++++++++++++++
 src/include/catalog/catversion.h          |   2 +-
 src/include/catalog/pg_amop.dat           | 116 ++++
 src/include/catalog/pg_amproc.dat         | 447 ++++++++++++
 src/include/catalog/pg_opclass.dat        |  72 ++
 src/include/catalog/pg_opfamily.dat       |  38 +
 src/include/catalog/pg_proc.dat           |  34 +
 src/include/catalog/pg_type.dat           |   7 +-
 src/test/regress/expected/brin_bloom.out  | 428 ++++++++++++
 src/test/regress/expected/opr_sanity.out  |   3 +-
 src/test/regress/expected/psql.out        |   3 +-
 src/test/regress/expected/type_sanity.out |   7 +-
 src/test/regress/parallel_schedule        |   5 +
 src/test/regress/serial_schedule          |   1 +
 src/test/regress/sql/brin_bloom.sql       | 376 ++++++++++
 17 files changed, 2567 insertions(+), 8 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 078f51bb55..cb4f9b08b9 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -115,7 +115,8 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
   operator classes store the minimum and the maximum values appearing
   in the indexed column within the range.  The <firstterm>inclusion</firstterm>
   operator classes store a value which includes the values in the indexed
-  column within the range.
+  column within the range.  The <firstterm>bloom</firstterm> operator
+  classes build a Bloom filter for all values in the range.
  </para>
 
  <table id="brin-builtin-opclasses-table">
@@ -154,6 +155,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row>
     <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry>
+     <entry><literal>= (character,character)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry>
      <entry><literal>= (character,character)</literal></entry>
@@ -163,6 +169,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (character,character)</literal></entry></row>
     <row><entry><literal>&gt;= (character,character)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>bytea_bloom_ops</literal></entry>
+     <entry><literal>= (bytea,bytea)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry>
      <entry><literal>= (bytea,bytea)</literal></entry>
@@ -172,6 +183,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row>
     <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>char_bloom_ops</literal></entry>
+     <entry><literal>= ("char","char")</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry>
      <entry><literal>= ("char","char")</literal></entry>
@@ -181,6 +197,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; ("char","char")</literal></entry></row>
     <row><entry><literal>&gt;= ("char","char")</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>date_bloom_ops</literal></entry>
+     <entry><literal>= (date,date)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry>
      <entry><literal>= (date,date)</literal></entry>
@@ -190,6 +211,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (date,date)</literal></entry></row>
     <row><entry><literal>&gt;= (date,date)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>float4_bloom_ops</literal></entry>
+     <entry><literal>= (float4,float4)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry>
      <entry><literal>= (float4,float4)</literal></entry>
@@ -199,6 +225,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (float4,float4)</literal></entry></row>
     <row><entry><literal>&gt;= (float4,float4)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>float8_bloom_ops</literal></entry>
+     <entry><literal>= (float8,float8)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry>
      <entry><literal>= (float8,float8)</literal></entry>
@@ -218,6 +249,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>= (inet,inet)</literal></entry></row>
     <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>inet_bloom_ops</literal></entry>
+     <entry><literal>= (inet,inet)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry>
      <entry><literal>= (inet,inet)</literal></entry>
@@ -227,6 +263,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (inet,inet)</literal></entry></row>
     <row><entry><literal>&gt;= (inet,inet)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>int2_bloom_ops</literal></entry>
+     <entry><literal>= (int2,int2)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry>
      <entry><literal>= (int2,int2)</literal></entry>
@@ -236,6 +277,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (int2,int2)</literal></entry></row>
     <row><entry><literal>&gt;= (int2,int2)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>int4_bloom_ops</literal></entry>
+     <entry><literal>= (int4,int4)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry>
      <entry><literal>= (int4,int4)</literal></entry>
@@ -245,6 +291,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (int4,int4)</literal></entry></row>
     <row><entry><literal>&gt;= (int4,int4)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>int8_bloom_ops</literal></entry>
+     <entry><literal>= (bigint,bigint)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry>
      <entry><literal>= (bigint,bigint)</literal></entry>
@@ -254,6 +305,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row>
     <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>interval_bloom_ops</literal></entry>
+     <entry><literal>= (interval,interval)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry>
      <entry><literal>= (interval,interval)</literal></entry>
@@ -263,6 +319,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (interval,interval)</literal></entry></row>
     <row><entry><literal>&gt;= (interval,interval)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry>
+     <entry><literal>= (macaddr,macaddr)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry>
      <entry><literal>= (macaddr,macaddr)</literal></entry>
@@ -272,6 +333,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row>
     <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry>
+     <entry><literal>= (macaddr8,macaddr8)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry>
      <entry><literal>= (macaddr8,macaddr8)</literal></entry>
@@ -281,6 +347,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row>
     <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>name_bloom_ops</literal></entry>
+     <entry><literal>= (name,name)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry>
      <entry><literal>= (name,name)</literal></entry>
@@ -290,6 +361,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (name,name)</literal></entry></row>
     <row><entry><literal>&gt;= (name,name)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>numeric_bloom_ops</literal></entry>
+     <entry><literal>= (numeric,numeric)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry>
      <entry><literal>= (numeric,numeric)</literal></entry>
@@ -299,6 +375,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row>
     <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>oid_bloom_ops</literal></entry>
+     <entry><literal>= (oid,oid)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry>
      <entry><literal>= (oid,oid)</literal></entry>
@@ -308,6 +389,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (oid,oid)</literal></entry></row>
     <row><entry><literal>&gt;= (oid,oid)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry>
+     <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry>
      <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
@@ -335,6 +421,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row>
     <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>text_bloom_ops</literal></entry>
+     <entry><literal>= (text,text)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry>
      <entry><literal>= (text,text)</literal></entry>
@@ -344,6 +435,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (text,text)</literal></entry></row>
     <row><entry><literal>&gt;= (text,text)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>tid_bloom_ops</literal></entry>
+     <entry><literal>= (tid,tid)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry>
      <entry><literal>= (tid,tid)</literal></entry>
@@ -353,6 +449,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (tid,tid)</literal></entry></row>
     <row><entry><literal>&gt;= (tid,tid)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry>
+     <entry><literal>= (timestamp,timestamp)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry>
      <entry><literal>= (timestamp,timestamp)</literal></entry>
@@ -362,6 +463,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row>
     <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry>
+     <entry><literal>= (timestamptz,timestamptz)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry>
      <entry><literal>= (timestamptz,timestamptz)</literal></entry>
@@ -371,6 +477,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row>
     <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>time_bloom_ops</literal></entry>
+     <entry><literal>= (time,time)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry>
      <entry><literal>= (time,time)</literal></entry>
@@ -380,6 +491,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (time,time)</literal></entry></row>
     <row><entry><literal>&gt;= (time,time)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>timetz_bloom_ops</literal></entry>
+     <entry><literal>= (timetz,timetz)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry>
      <entry><literal>= (timetz,timetz)</literal></entry>
@@ -389,6 +505,11 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row>
     <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row>
 
+    <row>
+     <entry valign="middle"><literal>uuid_bloom_ops</literal></entry>
+     <entry><literal>= (uuid,uuid)</literal></entry>
+    </row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry>
      <entry><literal>= (uuid,uuid)</literal></entry>
@@ -409,6 +530,55 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
    </tbody>
   </tgroup>
  </table>
+
+  <sect2 id="brin-builtin-opclasses--parameters">
+   <title>Operator Class Parameters</title>
+
+   <para>
+    Some of the built-in operator classes allow specifying parameters affecting
+    behavior of the operator class.  Each operator class has its own set of
+    allowed parameters.  Only the <literal>bloom</literal> operator class
+    allows specifying parameters:
+   </para>
+
+   <para>
+    <acronym>bloom</acronym> operator classes accept these parameters:
+   </para>
+
+   <variablelist>
+   <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 value is <literal>-0.1</literal>, and
+     the minimum number of distinct non-null values is <literal>16</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
+     between 0.0001 and 0.25. The default value is 0.01, which is 1% false
+     positive rate.
+    </para>
+    </listitem>
+   </varlistentry>
+
+   </variablelist>
+  </sect2>
+
 </sect1>
 
 <sect1 id="brin-extensibility">
@@ -781,6 +951,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/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..2214fb4d0c
--- /dev/null
+++ b/src/backend/access/brin/brin_bloom.c
@@ -0,0 +1,809 @@
+/*
+ * 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 testing whether a given page range contains
+ * a particular value. Therefore, if we summarize each page range into a small
+ * bloom filter, we can easily (and cheaply) test whether it contains values
+ * we get later.
+ *
+ * The index only supports equality operators, similarly to hash indexes.
+ * 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 - i.e. it's 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, because we're working with
+ * hashes and not the original values. Everything is uint32.
+ *
+ * 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.
+ *
+ *
+ * hashing scheme
+ * --------------
+ *
+ * Bloom filters require a number of independent hash functions. There are
+ * different schemes how to construct them - for example we might use
+ * hash_uint32_extended with random seeds, but that seems fairly expensive.
+ * We use a scheme requiring only two functions described in this paper:
+ *
+ * Less Hashing, Same Performance:Building a Better Bloom Filter
+ * Adam Kirsch, Michael Mitzenmacher†, Harvard School of Engineering and
+ * Applied Sciences, Cambridge, Massachusetts [DOI 10.1002/rsa.20208]
+ *
+ * The two hash functions h1 and h2 are calculated using hard-coded seeds,
+ * and then combined using (h1 + i * h2) to generate the hash functions.
+ *
+ *
+ * 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.
+ *
+ * Although 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 (when "full" about half the bits is set
+ * to 1, randomly), compression can't help very much.
+ *
+ * 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 alter 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 should reduce the number of distinct values
+ * in the page range, making the filter smaller (with fixed false positive
+ * rate). Even for random data sets this should help, 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 distribute the space between those columns.
+ *
+ * The current logic, implemented in brin_bloom_get_ndistinct, attempts to
+ * make some basic sizing decisions, based on the size of BRIN ranges, and
+ * the maximum number of rows per range.
+ *
+ *
+ * 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_page.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. We only have one, which is
+ * used to calculate hash of the input value.
+ *
+ * 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
+
+/*
+ * 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 */
+
+/*
+ * Allowed range and default value for the false positive range. The exact
+ * values are somewhat arbitrary, but were chosen considering the various
+ * parameters (size of filter vs. page size, etc.).
+ *
+ * The lower the false-positive rate, the more accurate the filter is, but
+ * it also gets larger - at some point this eliminates the main advantage
+ * of BRIN indexes, which is the tiny size. At 0.01% the index is about
+ * 10% of the table (assuming 290 distinct values per 8kB page).
+ *
+ * On the other hand, as the false-positive rate increases, larger part of
+ * the table has to be scanned due to mismatches - at 25% we're probably
+ * close to sequential scan being cheaper.
+ */
+#define		BLOOM_MIN_FALSE_POSITIVE_RATE	0.0001	/* 0.01% fp rate */
+#define		BLOOM_MAX_FALSE_POSITIVE_RATE	0.25	/* 25% fp rate */
+#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)
+
+/*
+ * And estimate of the largest bloom we can fit onto a page. This is not
+ * a perfect guarantee, for a couple of reasons. For example, the row may
+ * be larger because the index has multiple columns.
+ */
+#define BloomMaxFilterSize \
+	MAXALIGN_DOWN(BLCKSZ - \
+				  (MAXALIGN(SizeOfPageHeaderData + \
+							sizeof(ItemIdData)) + \
+				   MAXALIGN(sizeof(BrinSpecialSpace)) + \
+				   SizeOfBrinTuple))
+
+/*
+ * Seeds used to calculate two hash functions h1 and h2, which are then used
+ * to generate k hashes using the (h1 + i * h2) scheme.
+ */
+#define BLOOM_SEED_1	0x71d924af
+#define BLOOM_SEED_2	0xba48b314
+
+/*
+ * 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).
+ *
+ * XXX We could implement "sparse" bloom filters, keeping only the bytes that
+ * are not entirely 0. But while indexes don't support TOAST, the varlena can
+ * still be compressed. So this seems unnecessary, because the compression
+ * should do the same job.
+ *
+ * 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. But even if the false positive rate exceeds the
+ * desired value, it still can eliminate some page ranges.
+ */
+typedef struct BloomFilter
+{
+	/* varlena header (do not touch directly!) */
+	int32		vl_len_;
+
+	/* space for various flags (unused for now) */
+	uint16		flags;
+
+	/* 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 */
+	char		data[FLEXIBLE_ARRAY_MEMBER];
+
+}			BloomFilter;
+
+
+/*
+ * bloom_init
+ * 		Initialize the Bloom Filter, allocate all the memory.
+ *
+ * The filter is initialized with optimal size for ndistinct expected values
+ * and the 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			nbits;			/* size of filter / number of bits */
+	int			nbytes;			/* size of filter / number of bytes */
+
+	double		k;				/* number of hash functions */
+
+	Assert(ndistinct > 0);
+	Assert((false_positive_rate >= BLOOM_MIN_FALSE_POSITIVE_RATE) &&
+		   (false_positive_rate < BLOOM_MAX_FALSE_POSITIVE_RATE));
+
+	/* sizing bloom filter: -(n * ln(p)) / (ln(2))^2 */
+	nbits = ceil(-(ndistinct * log(false_positive_rate)) / pow(log(2.0), 2));
+
+	/* round m to whole bytes */
+	nbytes = ((nbits + 7) / 8);
+	nbits = nbytes * 8;
+
+	/*
+	 * Reject filters that are obviously too large to store on a page.
+	 *
+	 * Initially the bloom filter is just zeroes and so very compressible, but
+	 * as we add values it gets more and more random, and so less and less
+	 * compressible. So initially everything fits on the page, but we might
+	 * get surprising failures later - we want to prevent that, so we reject
+	 * bloom filter that are obviously too large.
+	 *
+	 * XXX It's not uncommon to oversize the bloom filter a bit, to defend
+	 * against unexpected data anomalies (parts of table with more distinct
+	 * values per range etc.). But we still need to make sure even the
+	 * oversized filter fits on page, if such need arises.
+	 *
+	 * XXX This check is not perfect, because the index may have multiple
+	 * filters that are small individually, but too large when combined.
+	 */
+	if (nbytes > BloomMaxFilterSize)
+		elog(ERROR, "the bloom filter is too large (%d > %zu)", nbytes,
+			 BloomMaxFilterSize);
+
+	/*
+	 * round(log(2.0) * m / ndistinct), but assume round() may not be
+	 * available on Windows
+	 */
+	k = log(2.0) * nbits / ndistinct;
+	k = (k - floor(k) >= 0.5) ? ceil(k) : floor(k);
+
+	/*
+	 * We allocate the whole filter. Most of it is going to be 0 bits, so the
+	 * varlena is easy to compress.
+	 */
+	len = offsetof(BloomFilter, data) + nbytes;
+
+	filter = (BloomFilter *) palloc0(len);
+
+	filter->flags = 0;
+	filter->nhashes = (int) k;
+	filter->nbits = nbits;
+
+	SET_VARSIZE(filter, len);
+
+	return filter;
+}
+
+
+/*
+ * bloom_add_value
+ * 		Add value to the bloom filter.
+ */
+static BloomFilter *
+bloom_add_value(BloomFilter * filter, uint32 value, bool *updated)
+{
+	int			i;
+	uint64		h1,
+				h2;
+
+	/* compute the hashes, used for the bloom filter */
+	h1 = hash_bytes_uint32_extended(value, BLOOM_SEED_1) % filter->nbits;
+	h2 = hash_bytes_uint32_extended(value, BLOOM_SEED_2) % filter->nbits;
+
+	/* compute the requested number of hashes */
+	for (i = 0; i < filter->nhashes; i++)
+	{
+		/* h1 + h2 + f(i) */
+		uint32		h = (h1 + i * h2) % filter->nbits;
+		uint32		byte = (h / 8);
+		uint32		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;
+		}
+	}
+
+	return filter;
+}
+
+
+/*
+ * bloom_contains_value
+ * 		Check if the bloom filter contains a particular value.
+ */
+static bool
+bloom_contains_value(BloomFilter * filter, uint32 value)
+{
+	int			i;
+	uint64		h1,
+				h2;
+
+	/* calculate the two hashes */
+	h1 = hash_bytes_uint32_extended(value, BLOOM_SEED_1) % filter->nbits;
+	h2 = hash_bytes_uint32_extended(value, BLOOM_SEED_2) % filter->nbits;
+
+	/* compute the requested number of hashes */
+	for (i = 0; i < filter->nhashes; i++)
+	{
+		/* h1 + h2 + f(i) */
+		uint32		h = (h1 + i * h2) % filter->nbits;
+		uint32		byte = (h / 8);
+		uint32		bit = (h % 8);
+
+		/* if the bit is not set, the value is not there */
+		if (!(filter->data[byte] & (0x01 << bit)))
+			return false;
+	}
+
+	/* 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(PG_BRIN_BLOOM_SUMMARYOID, 0);
+
+	PG_RETURN_POINTER(result);
+}
+
+/*
+ * brin_bloom_get_ndistinct
+ *		Determine the ndistinct value used to size bloom filter.
+ *
+ * Adjust 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.
+ *
+ * XXX We might also calculate a better estimate of rows per BRIN range,
+ * instead of using MaxHeapTuplesPerPage (which probably produces values
+ * much higher than reality).
+ */
+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 to avoid using 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;
+}
+
+/*
+ * 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),
+							BloomGetFalsePositiveRate(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 for 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)
+{
+	int			i;
+	int			nbytes;
+	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 the filters use the same parameters */
+	Assert(filter_a && filter_b);
+	Assert(filter_a->nbits == filter_b->nbits);
+	Assert(filter_a->nhashes == filter_b->nhashes);
+	Assert((filter_a->nbits > 0) && (filter_a->nbits % 8 == 0));
+
+	nbytes = (filter_a->nbits) / 8;
+
+	/* simply OR the bitmaps */
+	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 does not exist.
+ */
+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,
+							 BLOOM_MIN_FALSE_POSITIVE_RATE,
+							 BLOOM_MAX_FALSE_POSITIVE_RATE,
+							 offsetof(BloomOptions, falsePositiveRate));
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * brin_bloom_summary_in
+ *		- input routine for type brin_bloom_summary.
+ *
+ * brin_bloom_summary is only used internally to represent summaries
+ * in BRIN bloom indexes, so it has no operations of its own, and we
+ * disallow input too.
+ */
+Datum
+brin_bloom_summary_in(PG_FUNCTION_ARGS)
+{
+	/*
+	 * brin_bloom_summary stores the data in binary form and parsing text
+	 * input is not needed, so disallow this.
+	 */
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+
+/*
+ * brin_bloom_summary_out
+ *		- output routine for type brin_bloom_summary.
+ *
+ * BRIN bloom summaries are serialized into a bytea value, but we want
+ * to output something nicer humans can understand.
+ */
+Datum
+brin_bloom_summary_out(PG_FUNCTION_ARGS)
+{
+	BloomFilter *filter;
+	StringInfoData str;
+
+	/* detoast the data to get value with a full 4B header */
+	filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+
+	initStringInfo(&str);
+	appendStringInfoChar(&str, '{');
+
+	appendStringInfo(&str, "mode: hashed  nhashes: %u  nbits: %u  nbits_set: %u",
+					 filter->nhashes, filter->nbits, filter->nbits_set);
+
+	appendStringInfoChar(&str, '}');
+
+	PG_RETURN_CSTRING(str.data);
+}
+
+/*
+ * brin_bloom_summary_recv
+ *		- binary input routine for type brin_bloom_summary.
+ */
+Datum
+brin_bloom_summary_recv(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type %s", "pg_brin_bloom_summary")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * brin_bloom_summary_send
+ *		- binary output routine for type brin_bloom_summary.
+ *
+ * BRIN bloom summaries are serialized in a bytea value (although the
+ * type is named differently), so let's just send that.
+ */
+Datum
+brin_bloom_summary_send(PG_FUNCTION_ARGS)
+{
+	return byteasend(fcinfo);
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 3cf93fd381..2f18734235 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202103231
+#define CATALOG_VERSION_NO	202103232
 
 #endif
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 0f7ff63669..04d678f96a 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1814,6 +1814,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)',
@@ -1831,6 +1836,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)',
@@ -1848,6 +1858,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',
@@ -1994,6 +2009,20 @@
   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 => 'int2',
+  amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)',
+  amopmethod => 'brin' },
+
+{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4',
+  amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)',
+  amopmethod => 'brin' },
+
 # minmax text
 { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text',
   amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)',
@@ -2011,6 +2040,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)',
@@ -2028,6 +2062,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)',
@@ -2045,6 +2084,11 @@
   amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)',
   amopmethod => 'brin' },
 
+# tid oid
+{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid',
+  amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)',
+  amopmethod => 'brin' },
+
 # minmax float (float4, float8)
 
 { amopfamily => 'brin/float_minmax_ops', amoplefttype => 'float4',
@@ -2111,6 +2155,14 @@
   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 => 'float8',
+  amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)',
+  amopmethod => 'brin' },
+
 # minmax macaddr
 { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr',
   amoprighttype => 'macaddr', amopstrategy => '1',
@@ -2128,6 +2180,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',
@@ -2145,6 +2202,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)',
@@ -2162,6 +2224,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)',
@@ -2199,6 +2266,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)',
@@ -2216,6 +2288,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',
@@ -2362,6 +2439,20 @@
   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 => 'date',
+  amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)',
+  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',
@@ -2379,6 +2470,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)',
@@ -2396,6 +2492,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)',
@@ -2447,6 +2548,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)',
@@ -2464,6 +2570,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',
@@ -2525,6 +2636,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 36b5235c80..2af8af3f4e 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -805,6 +805,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',
@@ -818,6 +836,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',
@@ -831,6 +867,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',
@@ -932,6 +986,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',
@@ -945,6 +1051,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' },
@@ -957,6 +1081,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' },
@@ -969,6 +1110,23 @@
 { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid',
   amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' },
 
+# bloom tid
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '2',
+  amproc => 'brin_bloom_add_value' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '3',
+  amproc => 'brin_bloom_consistent' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '5',
+  amproc => 'brin_bloom_options' },
+{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' },
+
 # minmax float
 { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4',
   amprocrighttype => 'float4', amprocnum => '1',
@@ -1019,6 +1177,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',
@@ -1033,6 +1230,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',
@@ -1047,6 +1264,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',
@@ -1060,6 +1297,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',
@@ -1094,6 +1349,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 => 'hashbpchar' },
+
 # minmax time without time zone
 { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time',
   amprocrighttype => 'time', amprocnum => '1',
@@ -1107,6 +1382,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',
@@ -1214,6 +1507,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',
@@ -1228,6 +1577,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',
@@ -1242,6 +1611,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' },
@@ -1282,6 +1671,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',
@@ -1295,6 +1704,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',
@@ -1332,6 +1759,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 24b1433e1f..6a5bb58baf 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -266,67 +266,130 @@
 { opcmethod => 'brin', opcname => 'bytea_minmax_ops',
   opcfamily => 'brin/bytea_minmax_ops', opcintype => 'bytea',
   opckeytype => 'bytea' },
+{ opcmethod => 'brin', opcname => 'bytea_bloom_ops',
+  opcfamily => 'brin/bytea_bloom_ops', opcintype => 'bytea',
+  opckeytype => 'bytea', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'char_minmax_ops',
   opcfamily => 'brin/char_minmax_ops', opcintype => 'char',
   opckeytype => 'char' },
+{ opcmethod => 'brin', opcname => 'char_bloom_ops',
+  opcfamily => 'brin/char_bloom_ops', opcintype => 'char',
+  opckeytype => 'char', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'name_minmax_ops',
   opcfamily => 'brin/name_minmax_ops', opcintype => 'name',
   opckeytype => 'name' },
+{ opcmethod => 'brin', opcname => 'name_bloom_ops',
+  opcfamily => 'brin/name_bloom_ops', opcintype => 'name',
+  opckeytype => 'name', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'int8_minmax_ops',
   opcfamily => 'brin/integer_minmax_ops', opcintype => 'int8',
   opckeytype => 'int8' },
+{ opcmethod => 'brin', opcname => 'int8_bloom_ops',
+  opcfamily => 'brin/integer_bloom_ops', opcintype => 'int8',
+  opckeytype => 'int8', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'int2_minmax_ops',
   opcfamily => 'brin/integer_minmax_ops', opcintype => 'int2',
   opckeytype => 'int2' },
+{ opcmethod => 'brin', opcname => 'int2_bloom_ops',
+  opcfamily => 'brin/integer_bloom_ops', opcintype => 'int2',
+  opckeytype => 'int2', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'int4_minmax_ops',
   opcfamily => 'brin/integer_minmax_ops', opcintype => 'int4',
   opckeytype => 'int4' },
+{ opcmethod => 'brin', opcname => 'int4_bloom_ops',
+  opcfamily => 'brin/integer_bloom_ops', opcintype => 'int4',
+  opckeytype => 'int4', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'text_minmax_ops',
   opcfamily => 'brin/text_minmax_ops', opcintype => 'text',
   opckeytype => 'text' },
+{ opcmethod => 'brin', opcname => 'text_bloom_ops',
+  opcfamily => 'brin/text_bloom_ops', opcintype => 'text',
+  opckeytype => 'text', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'oid_minmax_ops',
   opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' },
+{ opcmethod => 'brin', opcname => 'oid_bloom_ops',
+  opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid',
+  opckeytype => 'oid', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'tid_minmax_ops',
   opcfamily => 'brin/tid_minmax_ops', opcintype => 'tid', opckeytype => 'tid' },
+{ opcmethod => 'brin', opcname => 'tid_bloom_ops',
+  opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid',
+  opcdefault => 'f'},
 { opcmethod => 'brin', opcname => 'float4_minmax_ops',
   opcfamily => 'brin/float_minmax_ops', opcintype => 'float4',
   opckeytype => 'float4' },
+{ opcmethod => 'brin', opcname => 'float4_bloom_ops',
+  opcfamily => 'brin/float_bloom_ops', opcintype => 'float4',
+  opckeytype => 'float4', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'float8_minmax_ops',
   opcfamily => 'brin/float_minmax_ops', opcintype => 'float8',
   opckeytype => 'float8' },
+{ opcmethod => 'brin', opcname => 'float8_bloom_ops',
+  opcfamily => 'brin/float_bloom_ops', opcintype => 'float8',
+  opckeytype => 'float8', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'macaddr_minmax_ops',
   opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr',
   opckeytype => 'macaddr' },
+{ opcmethod => 'brin', opcname => 'macaddr_bloom_ops',
+  opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr',
+  opckeytype => 'macaddr', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops',
   opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8',
   opckeytype => 'macaddr8' },
+{ opcmethod => 'brin', opcname => 'macaddr8_bloom_ops',
+  opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8',
+  opckeytype => 'macaddr8', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'inet_minmax_ops',
   opcfamily => 'brin/network_minmax_ops', opcintype => 'inet',
   opcdefault => 'f', opckeytype => 'inet' },
+{ opcmethod => 'brin', opcname => 'inet_bloom_ops',
+  opcfamily => 'brin/network_bloom_ops', opcintype => 'inet',
+  opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'inet_inclusion_ops',
   opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet',
   opckeytype => 'inet' },
 { opcmethod => 'brin', opcname => 'bpchar_minmax_ops',
   opcfamily => 'brin/bpchar_minmax_ops', opcintype => 'bpchar',
   opckeytype => 'bpchar' },
+{ opcmethod => 'brin', opcname => 'bpchar_bloom_ops',
+  opcfamily => 'brin/bpchar_bloom_ops', opcintype => 'bpchar',
+  opckeytype => 'bpchar', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'time_minmax_ops',
   opcfamily => 'brin/time_minmax_ops', opcintype => 'time',
   opckeytype => 'time' },
+{ opcmethod => 'brin', opcname => 'time_bloom_ops',
+  opcfamily => 'brin/time_bloom_ops', opcintype => 'time',
+  opckeytype => 'time', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'date_minmax_ops',
   opcfamily => 'brin/datetime_minmax_ops', opcintype => 'date',
   opckeytype => 'date' },
+{ opcmethod => 'brin', opcname => 'date_bloom_ops',
+  opcfamily => 'brin/datetime_bloom_ops', opcintype => 'date',
+  opckeytype => 'date', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'timestamp_minmax_ops',
   opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamp',
   opckeytype => 'timestamp' },
+{ opcmethod => 'brin', opcname => 'timestamp_bloom_ops',
+  opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamp',
+  opckeytype => 'timestamp', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'timestamptz_minmax_ops',
   opcfamily => 'brin/datetime_minmax_ops', opcintype => 'timestamptz',
   opckeytype => 'timestamptz' },
+{ opcmethod => 'brin', opcname => 'timestamptz_bloom_ops',
+  opcfamily => 'brin/datetime_bloom_ops', opcintype => 'timestamptz',
+  opckeytype => 'timestamptz', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'interval_minmax_ops',
   opcfamily => 'brin/interval_minmax_ops', opcintype => 'interval',
   opckeytype => 'interval' },
+{ opcmethod => 'brin', opcname => 'interval_bloom_ops',
+  opcfamily => 'brin/interval_bloom_ops', opcintype => 'interval',
+  opckeytype => 'interval', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'timetz_minmax_ops',
   opcfamily => 'brin/timetz_minmax_ops', opcintype => 'timetz',
   opckeytype => 'timetz' },
+{ opcmethod => 'brin', opcname => 'timetz_bloom_ops',
+  opcfamily => 'brin/timetz_bloom_ops', opcintype => 'timetz',
+  opckeytype => 'timetz', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'bit_minmax_ops',
   opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' },
 { opcmethod => 'brin', opcname => 'varbit_minmax_ops',
@@ -335,18 +398,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 57e5aa0d8b..7cc3d59a8c 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -182,50 +182,88 @@
   opfmethod => 'gin', opfname => 'jsonb_path_ops' },
 { oid => '4054',
   opfmethod => 'brin', opfname => 'integer_minmax_ops' },
+{ oid => '4572',
+  opfmethod => 'brin', opfname => 'integer_bloom_ops' },
 { oid => '4055',
   opfmethod => 'brin', opfname => 'numeric_minmax_ops' },
 { oid => '4056',
   opfmethod => 'brin', opfname => 'text_minmax_ops' },
+{ oid => '4573',
+  opfmethod => 'brin', opfname => 'text_bloom_ops' },
+{ oid => '4574',
+  opfmethod => 'brin', opfname => 'numeric_bloom_ops' },
 { oid => '4058',
   opfmethod => 'brin', opfname => 'timetz_minmax_ops' },
+{ oid => '4575',
+  opfmethod => 'brin', opfname => 'timetz_bloom_ops' },
 { oid => '4059',
   opfmethod => 'brin', opfname => 'datetime_minmax_ops' },
+{ oid => '4576',
+  opfmethod => 'brin', opfname => 'datetime_bloom_ops' },
 { oid => '4062',
   opfmethod => 'brin', opfname => 'char_minmax_ops' },
+{ oid => '4577',
+  opfmethod => 'brin', opfname => 'char_bloom_ops' },
 { oid => '4064',
   opfmethod => 'brin', opfname => 'bytea_minmax_ops' },
+{ oid => '4578',
+  opfmethod => 'brin', opfname => 'bytea_bloom_ops' },
 { oid => '4065',
   opfmethod => 'brin', opfname => 'name_minmax_ops' },
+{ oid => '4579',
+  opfmethod => 'brin', opfname => 'name_bloom_ops' },
 { oid => '4068',
   opfmethod => 'brin', opfname => 'oid_minmax_ops' },
+{ oid => '4580',
+  opfmethod => 'brin', opfname => 'oid_bloom_ops' },
 { oid => '4069',
   opfmethod => 'brin', opfname => 'tid_minmax_ops' },
+{ oid => '4581',
+  opfmethod => 'brin', opfname => 'tid_bloom_ops' },
 { oid => '4070',
   opfmethod => 'brin', opfname => 'float_minmax_ops' },
+{ oid => '4582',
+  opfmethod => 'brin', opfname => 'float_bloom_ops' },
 { oid => '4074',
   opfmethod => 'brin', opfname => 'macaddr_minmax_ops' },
+{ oid => '4583',
+  opfmethod => 'brin', opfname => 'macaddr_bloom_ops' },
 { oid => '4109',
   opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' },
+{ oid => '4584',
+  opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' },
 { oid => '4075',
   opfmethod => 'brin', opfname => 'network_minmax_ops' },
 { oid => '4102',
   opfmethod => 'brin', opfname => 'network_inclusion_ops' },
+{ oid => '4585',
+  opfmethod => 'brin', opfname => 'network_bloom_ops' },
 { oid => '4076',
   opfmethod => 'brin', opfname => 'bpchar_minmax_ops' },
+{ oid => '4586',
+  opfmethod => 'brin', opfname => 'bpchar_bloom_ops' },
 { oid => '4077',
   opfmethod => 'brin', opfname => 'time_minmax_ops' },
+{ oid => '4587',
+  opfmethod => 'brin', opfname => 'time_bloom_ops' },
 { oid => '4078',
   opfmethod => 'brin', opfname => 'interval_minmax_ops' },
+{ oid => '4588',
+  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 => '4589',
+  opfmethod => 'brin', opfname => 'uuid_bloom_ops' },
 { oid => '4103',
   opfmethod => 'brin', opfname => 'range_inclusion_ops' },
 { oid => '4082',
   opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' },
+{ oid => '4590',
+  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 b9f4afba05..c89df733f7 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8238,6 +8238,26 @@
   proargtypes => 'internal internal internal',
   prosrc => 'brin_inclusion_union' },
 
+# BRIN bloom
+{ oid => '4591', descr => 'BRIN bloom support',
+  proname => 'brin_bloom_opcinfo', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' },
+{ oid => '4592', descr => 'BRIN bloom support',
+  proname => 'brin_bloom_add_value', prorettype => 'bool',
+  proargtypes => 'internal internal internal internal',
+  prosrc => 'brin_bloom_add_value' },
+{ oid => '4593', descr => 'BRIN bloom support',
+  proname => 'brin_bloom_consistent', prorettype => 'bool',
+  proargtypes => 'internal internal internal int4',
+  prosrc => 'brin_bloom_consistent' },
+{ oid => '4594', descr => 'BRIN bloom support',
+  proname => 'brin_bloom_union', prorettype => 'bool',
+  proargtypes => 'internal internal internal',
+  prosrc => 'brin_bloom_union' },
+{ oid => '4595', 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',
@@ -11411,4 +11431,18 @@
   proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'unicode_is_normalized' },
 
+{ oid => '4596', descr => 'I/O',
+  proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary',
+  proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' },
+{ oid => '4597', descr => 'I/O',
+  proname => 'brin_bloom_summary_out', prorettype => 'cstring',
+  proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' },
+{ oid => '4598', descr => 'I/O',
+  proname => 'brin_bloom_summary_recv', provolatile => 's',
+  prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal',
+  prosrc => 'brin_bloom_summary_recv' },
+{ oid => '4599', descr => 'I/O',
+  proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea',
+  proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' },
+
 ]
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 8959c2f53b..2a82a3e544 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -679,5 +679,10 @@
   typtype => 'p', typcategory => 'P', typinput => 'anycompatiblemultirange_in',
   typoutput => 'anycompatiblemultirange_out', typreceive => '-', typsend => '-',
   typalign => 'd', typstorage => 'x' },
-
+{ oid => '4600',
+  descr => 'BRIN bloom summary',
+  typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S',
+  typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out',
+  typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send',
+  typalign => 'i', typstorage => 'x', typcollation => 'default' },
 ]
diff --git a/src/test/regress/expected/brin_bloom.out b/src/test/regress/expected/brin_bloom.out
new file mode 100644
index 0000000000..32c56a996a
--- /dev/null
+++ b/src/test/regress/expected/brin_bloom.out
@@ -0,0 +1,428 @@
+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.0001 and 0.25
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+	byteacol bytea_bloom_ops(false_positive_rate = 0.00009)
+);
+ERROR:  value 0.00009 out of bounds for option "false_positive_rate"
+DETAIL:  Valid values are between "0.000100" and "0.250000".
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+	byteacol bytea_bloom_ops(false_positive_rate = 0.26)
+);
+ERROR:  value 0.26 out of bounds for option "false_positive_rate"
+DETAIL:  Valid values are between "0.000100" and "0.250000".
+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}'),
+	('int4col', 'int4',
+	 '{=}',
+	 '{800}',
+	 '{1}'),
+	('int8col', 'int8',
+	 '{=}',
+	 '{1257141600}',
+	 '{1}'),
+	('textcol', 'text',
+	 '{=}',
+	 '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}',
+	 '{1}'),
+	('oidcol', 'oid',
+	 '{=}',
+	 '{8800}',
+	 '{1}'),
+	('float4col', 'float4',
+	 '{=}',
+	 '{1}',
+	 '{4}'),
+	('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}'),
+	('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 254ca06d3d..ef4b4444b9 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2037,6 +2037,7 @@ ORDER BY 1, 2, 3;
        2742 |           16 | @@
        3580 |            1 | <
        3580 |            1 | <<
+       3580 |            1 | =
        3580 |            2 | &<
        3580 |            2 | <=
        3580 |            3 | &&
@@ -2100,7 +2101,7 @@ ORDER BY 1, 2, 3;
        4000 |           28 | ^@
        4000 |           29 | <^
        4000 |           30 | >^
-(123 rows)
+(124 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 7204fdb0b4..a7a5b22d4f 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4993,8 +4993,9 @@ List of access methods
                    List of operator classes
   AM  | Input type | Storage type | Operator class | Default? 
 ------+------------+--------------+----------------+----------
+ brin | oid        |              | oid_bloom_ops  | no
  brin | oid        |              | oid_minmax_ops | yes
-(1 row)
+(2 rows)
 
 \dAf spgist
           List of operator families
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 0c74dc96a8..48ce3f7411 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -67,13 +67,14 @@ WHERE p1.typtype not in ('p') AND p1.typname NOT LIKE E'\\_%'
      WHERE p2.typname = ('_' || p1.typname)::name AND
            p2.typelem = p1.oid and p1.typarray = p2.oid)
 ORDER BY p1.oid;
- oid  |     typname     
-------+-----------------
+ oid  |        typname        
+------+-----------------------
   194 | pg_node_tree
  3361 | pg_ndistinct
  3402 | pg_dependencies
+ 4600 | pg_brin_bloom_summary
  5017 | pg_mcv_list
-(4 rows)
+(5 rows)
 
 -- Make sure typarray points to a "true" array type of our own base
 SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 70c38309d7..95927a7bae 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -77,6 +77,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 d81d04136c..c02a981c78 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -108,6 +108,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..5d499208e3
--- /dev/null
+++ b/src/test/regress/sql/brin_bloom.sql
@@ -0,0 +1,376 @@
+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.0001 and 0.25
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+	byteacol bytea_bloom_ops(false_positive_rate = 0.00009)
+);
+CREATE INDEX brinidx_bloom ON brintest_bloom USING brin (
+	byteacol bytea_bloom_ops(false_positive_rate = 0.26)
+);
+
+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}'),
+	('int4col', 'int4',
+	 '{=}',
+	 '{800}',
+	 '{1}'),
+	('int8col', 'int8',
+	 '{=}',
+	 '{1257141600}',
+	 '{1}'),
+	('textcol', 'text',
+	 '{=}',
+	 '{BNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAABNAAAA}',
+	 '{1}'),
+	('oidcol', 'oid',
+	 '{=}',
+	 '{8800}',
+	 '{1}'),
+	('float4col', 'float4',
+	 '{=}',
+	 '{1}',
+	 '{4}'),
+	('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}'),
+	('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.30.2

  [text/x-patch] 0003-BRIN-minmax-multi-indexes-20210323.patch (242.3K, ../357fb6d7-649f-822e-ff43-16b39aed68ee@enterprisedb.com/4-0003-BRIN-minmax-multi-indexes-20210323.patch)
  download | inline diff:
From 41912b7bd4db1eab6aaf63b0bec59a1cef8e271a Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Tue, 23 Mar 2021 01:04:06 +0100
Subject: [PATCH 3/5] BRIN minmax-multi indexes

Adds a BRIN minmax-multi opclass, summarizing each range into a list of
intervals, allowing more efficient handling of cases where the minmax
opclasses quickly degrade.

Instead of representing each range with a single interval, minmax-multi
opclasses store a list of intervals and points. This allows effective
handling of outliers and poorly correlated values.

The number of intervals/points per range may be configured using an
opclass parameter values_per_range. When building the summary, the
interval are merged based on distance, determined by the new support
BRIN procedure.

Author: Tomas Vondra <tomas.vondra@postgresql.org>
Reviewed-by: Alvaro Herrera <alvherre@alvh.no-ip.org>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Reviewed-by: Sokolov Yura <y.sokolov@postgrespro.ru>
Reviewed-by: John Naylor <john.naylor@enterprisedb.com>
Discussion: https://postgr.es/m/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com
---
 doc/src/sgml/brin.sgml                      |  280 +-
 src/backend/access/brin/Makefile            |    1 +
 src/backend/access/brin/brin_minmax_multi.c | 3036 +++++++++++++++++++
 src/backend/access/brin/brin_tuple.c        |   23 +-
 src/include/access/brin_tuple.h             |    8 +
 src/include/access/transam.h                |   10 +-
 src/include/catalog/catversion.h            |    2 +-
 src/include/catalog/pg_amop.dat             |  544 ++++
 src/include/catalog/pg_amproc.dat           |  600 +++-
 src/include/catalog/pg_opclass.dat          |   57 +
 src/include/catalog/pg_opfamily.dat         |   28 +
 src/include/catalog/pg_proc.dat             |   85 +
 src/include/catalog/pg_type.dat             |    6 +
 src/test/regress/expected/brin_multi.out    |  450 +++
 src/test/regress/expected/psql.out          |   13 +-
 src/test/regress/expected/type_sanity.out   |    7 +-
 src/test/regress/parallel_schedule          |    2 +-
 src/test/regress/serial_schedule            |    1 +
 src/test/regress/sql/brin_multi.sql         |  403 +++
 19 files changed, 5526 insertions(+), 30 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 cb4f9b08b9..3ca8236272 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -116,7 +116,10 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
   in the indexed column within the range.  The <firstterm>inclusion</firstterm>
   operator classes store a value which includes the values in the indexed
   column within the range.  The <firstterm>bloom</firstterm> operator
-  classes build a Bloom filter for all values in the range.
+  classes build a Bloom filter for all values in the range.  The
+  <firstterm>minmax-multi</firstterm> operator classes store multiple
+  minimum and maximum values, representing values appearing in the indexed
+  column within the range.
  </para>
 
  <table id="brin-builtin-opclasses-table">
@@ -211,6 +214,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (date,date)</literal></entry></row>
     <row><entry><literal>&gt;= (date,date)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry>
+     <entry><literal>= (date,date)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (date,date)</literal></entry></row>
+    <row><entry><literal>&lt;= (date,date)</literal></entry></row>
+    <row><entry><literal>&gt; (date,date)</literal></entry></row>
+    <row><entry><literal>&gt;= (date,date)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>float4_bloom_ops</literal></entry>
      <entry><literal>= (float4,float4)</literal></entry>
@@ -225,6 +237,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (float4,float4)</literal></entry></row>
     <row><entry><literal>&gt;= (float4,float4)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry>
+     <entry><literal>= (float4,float4)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (float4,float4)</literal></entry></row>
+    <row><entry><literal>&gt; (float4,float4)</literal></entry></row>
+    <row><entry><literal>&lt;= (float4,float4)</literal></entry></row>
+    <row><entry><literal>&gt;= (float4,float4)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>float8_bloom_ops</literal></entry>
      <entry><literal>= (float8,float8)</literal></entry>
@@ -239,6 +260,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (float8,float8)</literal></entry></row>
     <row><entry><literal>&gt;= (float8,float8)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry>
+     <entry><literal>= (float8,float8)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (float8,float8)</literal></entry></row>
+    <row><entry><literal>&lt;= (float8,float8)</literal></entry></row>
+    <row><entry><literal>&gt; (float8,float8)</literal></entry></row>
+    <row><entry><literal>&gt;= (float8,float8)</literal></entry></row>
+
     <row>
      <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry>
      <entry><literal>&lt;&lt; (inet,inet)</literal></entry>
@@ -263,6 +293,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (inet,inet)</literal></entry></row>
     <row><entry><literal>&gt;= (inet,inet)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry>
+     <entry><literal>= (inet,inet)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (inet,inet)</literal></entry></row>
+    <row><entry><literal>&lt;= (inet,inet)</literal></entry></row>
+    <row><entry><literal>&gt; (inet,inet)</literal></entry></row>
+    <row><entry><literal>&gt;= (inet,inet)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>int2_bloom_ops</literal></entry>
      <entry><literal>= (int2,int2)</literal></entry>
@@ -277,6 +316,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (int2,int2)</literal></entry></row>
     <row><entry><literal>&gt;= (int2,int2)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry>
+     <entry><literal>= (int2,int2)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (int2,int2)</literal></entry></row>
+    <row><entry><literal>&gt; (int2,int2)</literal></entry></row>
+    <row><entry><literal>&lt;= (int2,int2)</literal></entry></row>
+    <row><entry><literal>&gt;= (int2,int2)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>int4_bloom_ops</literal></entry>
      <entry><literal>= (int4,int4)</literal></entry>
@@ -291,6 +339,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (int4,int4)</literal></entry></row>
     <row><entry><literal>&gt;= (int4,int4)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry>
+     <entry><literal>= (int4,int4)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (int4,int4)</literal></entry></row>
+    <row><entry><literal>&gt; (int4,int4)</literal></entry></row>
+    <row><entry><literal>&lt;= (int4,int4)</literal></entry></row>
+    <row><entry><literal>&gt;= (int4,int4)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>int8_bloom_ops</literal></entry>
      <entry><literal>= (bigint,bigint)</literal></entry>
@@ -305,6 +362,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row>
     <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry>
+     <entry><literal>= (bigint,bigint)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row>
+    <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row>
+    <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row>
+    <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>interval_bloom_ops</literal></entry>
      <entry><literal>= (interval,interval)</literal></entry>
@@ -319,6 +385,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (interval,interval)</literal></entry></row>
     <row><entry><literal>&gt;= (interval,interval)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry>
+     <entry><literal>= (interval,interval)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (interval,interval)</literal></entry></row>
+    <row><entry><literal>&lt;= (interval,interval)</literal></entry></row>
+    <row><entry><literal>&gt; (interval,interval)</literal></entry></row>
+    <row><entry><literal>&gt;= (interval,interval)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry>
      <entry><literal>= (macaddr,macaddr)</literal></entry>
@@ -333,6 +408,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row>
     <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry>
+     <entry><literal>= (macaddr,macaddr)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row>
+    <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row>
+    <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row>
+    <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry>
      <entry><literal>= (macaddr8,macaddr8)</literal></entry>
@@ -347,6 +431,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row>
     <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry>
+     <entry><literal>= (macaddr8,macaddr8)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row>
+    <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row>
+    <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row>
+    <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>name_bloom_ops</literal></entry>
      <entry><literal>= (name,name)</literal></entry>
@@ -375,6 +468,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row>
     <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry>
+     <entry><literal>= (numeric,numeric)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row>
+    <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row>
+    <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row>
+    <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>oid_bloom_ops</literal></entry>
      <entry><literal>= (oid,oid)</literal></entry>
@@ -389,6 +491,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (oid,oid)</literal></entry></row>
     <row><entry><literal>&gt;= (oid,oid)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry>
+     <entry><literal>= (oid,oid)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (oid,oid)</literal></entry></row>
+    <row><entry><literal>&gt; (oid,oid)</literal></entry></row>
+    <row><entry><literal>&lt;= (oid,oid)</literal></entry></row>
+    <row><entry><literal>&gt;= (oid,oid)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry>
      <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
@@ -403,6 +514,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row>
     <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry>
+     <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row>
+    <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row>
+    <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row>
+    <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row>
+
     <row>
      <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry>
      <entry><literal>= (anyrange,anyrange)</literal></entry>
@@ -449,6 +569,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (tid,tid)</literal></entry></row>
     <row><entry><literal>&gt;= (tid,tid)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry>
+     <entry><literal>= (tid,tid)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (tid,tid)</literal></entry></row>
+    <row><entry><literal>&gt; (tid,tid)</literal></entry></row>
+    <row><entry><literal>&lt;= (tid,tid)</literal></entry></row>
+    <row><entry><literal>&gt;= (tid,tid)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry>
      <entry><literal>= (timestamp,timestamp)</literal></entry>
@@ -463,6 +592,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row>
     <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry>
+     <entry><literal>= (timestamp,timestamp)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row>
+    <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row>
+    <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row>
+    <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry>
      <entry><literal>= (timestamptz,timestamptz)</literal></entry>
@@ -477,6 +615,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row>
     <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry>
+     <entry><literal>= (timestamptz,timestamptz)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row>
+    <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row>
+    <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row>
+    <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>time_bloom_ops</literal></entry>
      <entry><literal>= (time,time)</literal></entry>
@@ -491,6 +638,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (time,time)</literal></entry></row>
     <row><entry><literal>&gt;= (time,time)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry>
+     <entry><literal>= (time,time)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (time,time)</literal></entry></row>
+    <row><entry><literal>&lt;= (time,time)</literal></entry></row>
+    <row><entry><literal>&gt; (time,time)</literal></entry></row>
+    <row><entry><literal>&gt;= (time,time)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>timetz_bloom_ops</literal></entry>
      <entry><literal>= (timetz,timetz)</literal></entry>
@@ -505,6 +661,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row>
     <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry>
+     <entry><literal>= (timetz,timetz)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row>
+    <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row>
+    <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row>
+    <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row>
+
     <row>
      <entry valign="middle"><literal>uuid_bloom_ops</literal></entry>
      <entry><literal>= (uuid,uuid)</literal></entry>
@@ -519,6 +684,15 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
     <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row>
     <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row>
 
+    <row>
+     <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry>
+     <entry><literal>= (uuid,uuid)</literal></entry>
+    </row>
+    <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row>
+    <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row>
+    <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row>
+    <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row>
+
     <row>
      <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry>
      <entry><literal>= (varbit,varbit)</literal></entry>
@@ -537,8 +711,8 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
    <para>
     Some of the built-in operator classes allow specifying parameters affecting
     behavior of the operator class.  Each operator class has its own set of
-    allowed parameters.  Only the <literal>bloom</literal> operator class
-    allows specifying parameters:
+    allowed parameters.  Only the <literal>bloom</literal> and <literal>minmax-multi</literal>
+    operator classes allow specifying parameters:
    </para>
 
    <para>
@@ -577,6 +751,25 @@ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was
    </varlistentry>
 
    </variablelist>
+
+   <para>
+    <acronym>minmax-multi</acronym> operator classes accept these parameters:
+   </para>
+
+   <variablelist>
+   <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
+     either a point, or a boundary of an interval. Values must be between
+     8 and 256, and the default value is 32.
+    </para>
+    </listitem>
+   </varlistentry>
+
+   </variablelist>
   </sect2>
 
 </sect1>
@@ -702,13 +895,14 @@ typedef struct BrinOpcInfo
     </varlistentry>
   </variablelist>
 
-  The core distribution includes support for two types of operator classes:
-  minmax and inclusion.  Operator class definitions using them are shipped for
-  in-core data types as appropriate.  Additional operator classes can be
-  defined by the user for other data types using equivalent definitions,
-  without having to write any source code; appropriate catalog entries being
-  declared is enough.  Note that assumptions about the semantics of operator
-  strategies are embedded in the support functions' source code.
+  The core distribution includes support for four types of operator classes:
+  minmax, minmax-multi, 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>
@@ -1005,6 +1199,72 @@ typedef struct BrinOpcInfo
     and return a hash of the value.
  </para>
 
+ <para>
+  The minmax-multi 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, minmax-multi allows summarization
+  into multiple smaller intervals to improve handling of outlier values.
+  It is possible to use the minmax-multi support procedures alongside the
+  corresponding operators, as shown in
+  <xref linkend="brin-extensibility-minmax-multi-table"/>.
+  All operator class members (procedures and operators) are mandatory.
+ </para>
+
+ <table id="brin-extensibility-minmax-multi-table">
+  <title>Procedure and Support Numbers for minmax-multi Operator Classes</title>
+  <tgroup cols="2">
+   <thead>
+    <row>
+     <entry>Operator class member</entry>
+     <entry>Object</entry>
+    </row>
+   </thead>
+   <tbody>
+    <row>
+     <entry>Support Procedure 1</entry>
+     <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry>
+    </row>
+    <row>
+     <entry>Support Procedure 2</entry>
+     <entry>internal function <function>brin_minmax_multi_add_value()</function></entry>
+    </row>
+    <row>
+     <entry>Support Procedure 3</entry>
+     <entry>internal function <function>brin_minmax_multi_consistent()</function></entry>
+    </row>
+    <row>
+     <entry>Support Procedure 4</entry>
+     <entry>internal function <function>brin_minmax_multi_union()</function></entry>
+    </row>
+    <row>
+     <entry>Support Procedure 11</entry>
+     <entry>function to compute distance between two values (length of a range)</entry>
+    </row>
+    <row>
+     <entry>Operator Strategy 1</entry>
+     <entry>operator less-than</entry>
+    </row>
+    <row>
+     <entry>Operator Strategy 2</entry>
+     <entry>operator less-than-or-equal-to</entry>
+    </row>
+    <row>
+     <entry>Operator Strategy 3</entry>
+     <entry>operator equal-to</entry>
+    </row>
+    <row>
+     <entry>Operator Strategy 4</entry>
+     <entry>operator greater-than-or-equal-to</entry>
+    </row>
+    <row>
+     <entry>Operator Strategy 5</entry>
+     <entry>operator greater-than</entry>
+    </row>
+   </tbody>
+  </tgroup>
+ </table>
+
  <para>
     Both minmax and inclusion operator classes support cross-data-type
     operators, though with these the dependencies become more complicated.
diff --git a/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..1ca86b7fb7
--- /dev/null
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -0,0 +1,3036 @@
+/*
+ * 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 make 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 minmax-multi 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 32 (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 50% 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.
+ *
+ * The compactions (reducing the number of values) is fairly expensive, as
+ * it requires calling the distance functions, sorting etc. So when building
+ * the summary, we use a significantly larger buffer, and only enforce the
+ * exact limit at the very end. This improves performance, and it also helps
+ * with building better ranges (due to the greedy approach).
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/brin/brin_minmax_multi.c
+ */
+#include "postgres.h"
+
+/* needef for PGSQL_AF_INET */
+#include <sys/socket.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_am.h"
+#include "catalog/pg_amop.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/datum.h"
+#include "utils/inet.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/numeric.h"
+#include "utils/pg_lsn.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+#include "utils/timestamp.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
+
+/*
+ * Sizing the insert buffer - we use 10x the number of values specified
+ * in the reloption, but we cap it to 8192 not to get too large. When
+ * the buffer gets full, we reduce the number of values by half.
+ */
+#define		MINMAX_BUFFER_FACTOR			10
+#define		MINMAX_BUFFER_MIN				256
+#define		MINMAX_BUFFER_MAX				8192
+#define		MINMAX_BUFFER_LOAD_FACTOR		0.5
+
+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 MinMaxMultiOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			valuesPerRange; /* number of values per range */
+} MinMaxMultiOptions;
+
+#define MINMAX_MULTI_DEFAULT_VALUES_PER_PAGE           32
+
+#define MinMaxMultiGetValuesPerRange(opts) \
+		((opts) && (((MinMaxMultiOptions *) (opts))->valuesPerRange != 0) ? \
+		 ((MinMaxMultiOptions *) (opts))->valuesPerRange : \
+		 MINMAX_MULTI_DEFAULT_VALUES_PER_PAGE)
+
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
+
+/*
+ * The summary of minmax-multi 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
+{
+	/* Cache information that we need quite often. */
+	Oid			typid;
+	Oid			colloid;
+	AttrNumber	attno;
+	FmgrInfo   *cmp;
+
+	/* (2*nranges + nvalues) <= maxvalues */
+	int			nranges;		/* number of ranges in the array (stored) */
+	int			nsorted;		/* number of sorted values (ranges + points) */
+	int			nvalues;		/* number of values in the data array (all) */
+	int			maxvalues;		/* maximum number of values (reloption) */
+
+	/*
+	 * We simply add the values into a large buffer, without any expensive
+	 * steps (sorting, deduplication, ...). The buffer is a multiple of the
+	 * target number of values, so the compaction happen less often,
+	 * amortizing the costs. We keep the actual target and compact to the
+	 * requested number of values at the very end, before serializing to
+	 * on-disk representation.
+	 */
+	/* requested number of values */
+	int			target_maxvalues;
+
+	/* 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, with a simple header
+ * with basic metadata, followed by the boundary values. It has a varlena
+ * header, so can be treated as varlena directly.
+ *
+ * See range_serialize/range_deserialize for serialization details.
+ */
+typedef struct SerializedRanges
+{
+	/* varlena header (do not touch directly!) */
+	int32		vl_len_;
+
+	/* type of values stored in the data array */
+	Oid			typid;
+
+	/* (2*nranges + nvalues) <= maxvalues */
+	int			nranges;		/* number of ranges in the array (stored) */
+	int			nvalues;		/* number of values in the data array (all) */
+	int			maxvalues;		/* maximum number of values (reloption) */
+
+	/* contains the actual data */
+	char		data[FLEXIBLE_ARRAY_MEMBER];
+} SerializedRanges;
+
+static SerializedRanges *range_serialize(Ranges *range);
+
+static Ranges *range_deserialize(int maxvalues, SerializedRanges *range);
+
+
+/*
+ * Used to represent ranges expanded to make merging and combining easier.
+ *
+ * Each expanded range is essentially an interval, represented by min/max
+ * values, along with a flag whether it's a collapsed range (in which case
+ * the min and max values are equal). We have the flag to handle by-ref
+ * data types - we can't simply compare the datums, and this saves some
+ * calls to the type-specific comparator function.
+ */
+typedef struct ExpandedRange
+{
+	Datum		minval;			/* lower boundary */
+	Datum		maxval;			/* upper boundary */
+	bool		collapsed;		/* true if minval==maxval */
+} ExpandedRange;
+
+/*
+ * Represents a distance between two ranges (identified by index into
+ * an array of extended ranges).
+ */
+typedef struct DistanceValue
+{
+	int			index;
+	double		value;
+} DistanceValue;
+
+
+/* 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);
+
+typedef struct compare_context
+{
+	FmgrInfo   *cmpFn;
+	Oid			colloid;
+}			compare_context;
+
+static int	compare_values(const void *a, const void *b, void *arg);
+
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * 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)
+{
+	int			i;
+	Datum		lt;
+
+	for (i = 0; i < (nvalues - 1); i++)
+	{
+		lt = FunctionCall2Coll(cmp, colloid, values[i], values[i + 1]);
+		Assert(DatumGetBool(lt));
+	}
+}
+#endif
+
+/*
+ * Comprehensive check of the Ranges structure.
+ */
+static void
+AssertCheckRanges(Ranges *ranges, FmgrInfo *cmpFn, Oid colloid)
+{
+#ifdef USE_ASSERT_CHECKING
+	int			i;
+
+	/* some basic sanity checks */
+	Assert(ranges->nranges >= 0);
+	Assert(ranges->nsorted >= 0);
+	Assert(ranges->nvalues >= ranges->nsorted);
+	Assert(ranges->maxvalues >= 2 * ranges->nranges + ranges->nvalues);
+	Assert(ranges->typid != InvalidOid);
+
+	/*
+	 * First the ranges - there are 2*nranges boundary values, and the values
+	 * have to be strictly ordered (equal values would mean the range is
+	 * collapsed, and should be stored as a point). This also guarantees that
+	 * the ranges do not overlap.
+	 */
+	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->nsorted);
+
+	/*
+	 * Check that none of the values are not covered by ranges (both sorted
+	 * and unsorted)
+	 */
+	for (i = 0; i < ranges->nvalues; i++)
+	{
+		Datum		compar;
+		int			start,
+					end;
+		Datum		minvalue,
+					maxvalue;
+
+		Datum		value = ranges->values[2 * ranges->nranges + i];
+
+		if (ranges->nranges == 0)
+			break;
+
+		minvalue = ranges->values[0];
+		maxvalue = ranges->values[2 * ranges->nranges - 1];
+
+		/*
+		 * Is the value smaller than the minval? If yes, we'll recurse to the
+		 * left side of range array.
+		 */
+		compar = FunctionCall2Coll(cmpFn, colloid, value, minvalue);
+
+		/* smaller than the smallest value in the first range */
+		if (DatumGetBool(compar))
+			continue;
+
+		/*
+		 * Is the value greater than the maxval? If yes, we'll recurse to the
+		 * right side of range array.
+		 */
+		compar = FunctionCall2Coll(cmpFn, colloid, maxvalue, value);
+
+		/* larger than the largest value in the last range */
+		if (DatumGetBool(compar))
+			continue;
+
+		start = 0;				/* first range */
+		end = ranges->nranges - 1;	/* last range */
+		while (true)
+		{
+			int			midpoint = (start + end) / 2;
+
+			/* this means we ran out of ranges in the last step */
+			if (start > end)
+				break;
+
+			/* copy the min/max values from the ranges */
+			minvalue = ranges->values[2 * midpoint];
+			maxvalue = ranges->values[2 * midpoint + 1];
+
+			/*
+			 * Is the value smaller than the minval? If yes, we'll recurse to
+			 * the left side of range array.
+			 */
+			compar = FunctionCall2Coll(cmpFn, colloid, value, minvalue);
+
+			/* smaller than the smallest value in this range */
+			if (DatumGetBool(compar))
+			{
+				end = (midpoint - 1);
+				continue;
+			}
+
+			/*
+			 * Is the value greater than the minval? If yes, we'll recurse to
+			 * the right side of range array.
+			 */
+			compar = FunctionCall2Coll(cmpFn, colloid, maxvalue, value);
+
+			/* larger than the largest value in this range */
+			if (DatumGetBool(compar))
+			{
+				start = (midpoint + 1);
+				continue;
+			}
+
+			/* hey, we found a matching range */
+			Assert(false);
+		}
+	}
+
+	/* and values in the unsorted part must not be in sorted part */
+	for (i = ranges->nsorted; i < ranges->nvalues; i++)
+	{
+		compare_context cxt;
+		Datum		value = ranges->values[2 * ranges->nranges + i];
+
+		if (ranges->nsorted == 0)
+			break;
+
+		cxt.colloid = ranges->colloid;
+		cxt.cmpFn = ranges->cmp;
+
+		Assert(bsearch_arg(&value, &ranges->values[2 * ranges->nranges],
+						   ranges->nsorted, sizeof(Datum),
+						   compare_values, (void *) &cxt) == NULL);
+	}
+#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
+AssertCheckExpandedRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno,
+						  Form_pg_attribute attr, ExpandedRange *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
+}
+
+
+/*
+ * minmax_multi_init
+ * 		Initialize the deserialized range list, allocate all the memory.
+ *
+ * This is only in-memory representation of the ranges, so we allocate
+ * 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_deduplicate_values
+ *		Deduplicate the part with values in the simple points.
+ *
+ * This is meant to be a cheaper way of reducing the size of the ranges. It
+ * does not touch the ranges, and only sorts the other values - it does not
+ * call the distance functions, which may be quite expensive, etc.
+ *
+ * We do know the values are not duplicate with the ranges, because we check
+ * that before adding a new value. Same for the sorted part of values.
+ */
+static void
+range_deduplicate_values(Ranges *range)
+{
+	int			i,
+				n;
+	int			start;
+	compare_context cxt;
+
+	/*
+	 * If there are no unsorted values, we're done (this probably can't
+	 * happen, as we're adding values to unsorted part).
+	 */
+	if (range->nsorted == range->nvalues)
+		return;
+
+	/* sort the values */
+	cxt.colloid = range->colloid;
+	cxt.cmpFn = range->cmp;
+
+	/* the values start right after the ranges (which are always sorted) */
+	start = 2 * range->nranges;
+
+	/*
+	 * XXX This might do a merge sort, to leverage that the first part of the
+	 * array is already sorted. If the sorted part is large, it might be quite
+	 * a bit faster.
+	 */
+	qsort_arg(&range->values[start],
+			  range->nvalues, sizeof(Datum),
+			  compare_values, (void *) &cxt);
+
+	n = 1;
+	for (i = 1; i < range->nvalues; i++)
+	{
+		/* same as preceding value, so store it */
+		if (compare_values(&range->values[start + i - 1],
+						   &range->values[start + i],
+						   (void *) &cxt) == 0)
+			continue;
+
+		range->values[start + n] = range->values[start + i];
+
+		n++;
+	}
+
+	/* now all the values are sorted */
+	range->nvalues = n;
+	range->nsorted = n;
+
+	AssertCheckRanges(range, range->cmp, range->colloid);
+}
+
+
+/*
+ * range_serialize
+ *	  Serialize the in-memory representation into a compact varlena value.
+ *
+ * Simply copy the header and then also the individual values, as stored
+ * in the in-memory value array.
+ */
+static SerializedRanges *
+range_serialize(Ranges *range)
+{
+	Size		len;
+	int			nvalues;
+	SerializedRanges *serialized;
+	Oid			typid;
+	int			typlen;
+	bool		typbyval;
+
+	int			i;
+	char	   *ptr;
+
+	/* simple sanity checks */
+	Assert(range->nranges >= 0);
+	Assert(range->nsorted >= 0);
+	Assert(range->nvalues >= 0);
+	Assert(range->maxvalues > 0);
+	Assert(range->target_maxvalues > 0);
+
+	/* at this point the range should be compacted to the target size */
+	Assert(2 * range->nranges + range->nvalues <= range->target_maxvalues);
+
+	Assert(range->target_maxvalues <= range->maxvalues);
+
+	/* range boundaries are always sorted */
+	Assert(range->nvalues >= range->nsorted);
+
+	/* deduplicate values, if there's unsorted part */
+	range_deduplicate_values(range);
+
+	/* see how many Datum values we actually have */
+	nvalues = 2 * range->nranges + range->nvalues;
+
+	typid = range->typid;
+	typbyval = get_typbyval(typid);
+	typlen = get_typlen(typid);
+
+	/* header is always needed */
+	len = offsetof(SerializedRanges, data);
+
+	/*
+	 * The space needed depends on data type - for fixed-length data types
+	 * (by-value and some by-reference) it's pretty simple, just multiply
+	 * (attlen * nvalues) and we're done. For variable-length by-reference
+	 * types we need to actually walk all the values and sum the lengths.
+	 */
+	if (typlen == -1)			/* varlena */
+	{
+		int			i;
+
+		for (i = 0; i < nvalues; i++)
+		{
+			len += VARSIZE_ANY(range->values[i]);
+		}
+	}
+	else if (typlen == -2)		/* cstring */
+	{
+		int			i;
+
+		for (i = 0; i < nvalues; i++)
+		{
+			/* don't forget to include the null terminator ;-) */
+			len += strlen(DatumGetPointer(range->values[i])) + 1;
+		}
+	}
+	else						/* fixed-length types (even by-reference) */
+	{
+		Assert(typlen > 0);
+		len += nvalues * typlen;
+	}
+
+	/*
+	 * Allocate the serialized object, copy the basic information. The
+	 * serialized object is a varlena, so update the header.
+	 */
+	serialized = (SerializedRanges *) palloc0(len);
+	SET_VARSIZE(serialized, len);
+
+	serialized->typid = typid;
+	serialized->nranges = range->nranges;
+	serialized->nvalues = range->nvalues;
+	serialized->maxvalues = range->target_maxvalues;
+
+	/*
+	 * And now copy also the boundary values (like the length calculation this
+	 * depends on the particular data type).
+	 */
+	ptr = serialized->data;		/* start of the serialized data */
+
+	for (i = 0; i < nvalues; i++)
+	{
+		if (typbyval)			/* simple by-value data types */
+		{
+			memcpy(ptr, &range->values[i], typlen);
+			ptr += typlen;
+		}
+		else if (typlen > 0)	/* fixed-length by-ref types */
+		{
+			memcpy(ptr, DatumGetPointer(range->values[i]), typlen);
+			ptr += typlen;
+		}
+		else if (typlen == -1)	/* varlena */
+		{
+			int			tmp = VARSIZE_ANY(DatumGetPointer(range->values[i]));
+
+			memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
+			ptr += tmp;
+		}
+		else if (typlen == -2)	/* cstring */
+		{
+			int			tmp = strlen(DatumGetPointer(range->values[i])) + 1;
+
+			memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
+			ptr += tmp;
+		}
+
+		/* make sure we haven't overflown the buffer end */
+		Assert(ptr <= ((char *) serialized + len));
+	}
+
+	/* exact size */
+	Assert(ptr == ((char *) serialized + len));
+
+	return serialized;
+}
+
+/*
+ * range_deserialize
+ *	  Serialize the in-memory representation into a compact varlena value.
+ *
+ * Simply copy the header and then also the individual values, as stored
+ * in the in-memory value array.
+ */
+static Ranges *
+range_deserialize(int maxvalues, SerializedRanges *serialized)
+{
+	int			i,
+				nvalues;
+	char	   *ptr;
+	bool		typbyval;
+	int			typlen;
+
+	Ranges	   *range;
+
+	Assert(serialized->nranges >= 0);
+	Assert(serialized->nvalues >= 0);
+	Assert(serialized->maxvalues > 0);
+
+	nvalues = 2 * serialized->nranges + serialized->nvalues;
+
+	Assert(nvalues <= serialized->maxvalues);
+	Assert(serialized->maxvalues <= maxvalues);
+
+	range = minmax_multi_init(maxvalues);
+
+	/* copy the header info */
+	range->nranges = serialized->nranges;
+	range->nvalues = serialized->nvalues;
+	range->nsorted = serialized->nvalues;
+	range->maxvalues = maxvalues;
+	range->target_maxvalues = serialized->maxvalues;
+
+	range->typid = serialized->typid;
+
+	typbyval = get_typbyval(serialized->typid);
+	typlen = get_typlen(serialized->typid);
+
+	/*
+	 * And now deconstruct the values into Datum array. We don't need to copy
+	 * the values and will instead just point the values to the serialized
+	 * varlena value (assuming it will be kept around).
+	 */
+	ptr = serialized->data;
+
+	for (i = 0; i < nvalues; i++)
+	{
+		if (typbyval)			/* simple by-value data types */
+		{
+			memcpy(&range->values[i], ptr, typlen);
+			ptr += typlen;
+		}
+		else if (typlen > 0)	/* fixed-length by-ref types */
+		{
+			/* no copy, just set the value to the pointer */
+			range->values[i] = PointerGetDatum(ptr);
+			ptr += typlen;
+		}
+		else if (typlen == -1)	/* varlena */
+		{
+			range->values[i] = PointerGetDatum(ptr);
+			ptr += VARSIZE_ANY(DatumGetPointer(range->values[i]));
+		}
+		else if (typlen == -2)	/* cstring */
+		{
+			range->values[i] = PointerGetDatum(ptr);
+			ptr += strlen(DatumGetPointer(range->values[i])) + 1;
+		}
+
+		/* make sure we haven't overflown the buffer end */
+		Assert(ptr <= ((char *) serialized + VARSIZE_ANY(serialized)));
+	}
+
+	/* should have consumed the whole input value exactly */
+	Assert(ptr == ((char *) serialized + VARSIZE_ANY(serialized)));
+
+	/* return the deserialized value */
+	return range;
+}
+
+/*
+ * compare_expanded_ranges
+ *	  Compare the expanded 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_expanded_ranges(const void *a, const void *b, void *arg)
+{
+	ExpandedRange *ra = (ExpandedRange *) a;
+	ExpandedRange *rb = (ExpandedRange *) 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;
+}
+
+/*
+ * compare_values
+ *	  Compare the values.
+ */
+static int
+compare_values(const void *a, const void *b, void *arg)
+{
+	Datum	   *da = (Datum *) a;
+	Datum	   *db = (Datum *) b;
+	Datum		r;
+
+	compare_context *cxt = (compare_context *) arg;
+
+	r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, *da, *db);
+
+	if (DatumGetBool(r))
+		return -1;
+
+	r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, *db, *da);
+
+	if (DatumGetBool(r))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Check if the new value matches one of the existing ranges.
+ */
+static bool
+has_matching_range(BrinDesc *bdesc, Oid colloid, Ranges *ranges,
+				   Datum newval, AttrNumber attno, Oid typid)
+{
+	Datum		compar;
+
+	Datum		minvalue = ranges->values[0];
+	Datum		maxvalue = ranges->values[2 * ranges->nranges - 1];
+
+	FmgrInfo   *cmpLessFn;
+	FmgrInfo   *cmpGreaterFn;
+
+	/* binary search on ranges */
+	int			start,
+				end;
+
+	if (ranges->nranges == 0)
+		return 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.
+	 */
+	cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+												   BTLessStrategyNumber);
+	compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue);
+
+	/* smaller than the smallest value in the range list */
+	if (DatumGetBool(compar))
+		return 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.
+	 */
+	cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+													  BTGreaterStrategyNumber);
+	compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue);
+
+	if (DatumGetBool(compar))
+		return false;
+
+	/*
+	 * So we know it's in the general min/max, the question is whether it
+	 * falls in one of the ranges or gaps. We'll do a binary search on
+	 * individual ranges - for each range we check equality (value falls into
+	 * the range), and then check ranges either above or below the current
+	 * range.
+	 */
+	start = 0;					/* first range */
+	end = (ranges->nranges - 1);	/* last range */
+	while (true)
+	{
+		int			midpoint = (start + end) / 2;
+
+		/* this means we ran out of ranges in the last step */
+		if (start > end)
+			return false;
+
+		/* copy the min/max values from the ranges */
+		minvalue = ranges->values[2 * midpoint];
+		maxvalue = ranges->values[2 * midpoint + 1];
+
+		/*
+		 * Is the value smaller than the minval? If yes, we'll recurse to the
+		 * left side of range array.
+		 */
+		compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue);
+
+		/* smaller than the smallest value in this range */
+		if (DatumGetBool(compar))
+		{
+			end = (midpoint - 1);
+			continue;
+		}
+
+		/*
+		 * Is the value greater than the minval? If yes, we'll recurse to the
+		 * right side of range array.
+		 */
+		compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue);
+
+		/* larger than the largest value in this range */
+		if (DatumGetBool(compar))
+		{
+			start = (midpoint + 1);
+			continue;
+		}
+
+		/* hey, we found a matching range */
+		return true;
+	}
+
+	return false;
+}
+
+
+/*
+ * 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.
+ *
+ * The last parameter (full) determines whether we need to search all the
+ * values, including the unsorted part. With full=false, the unsorted part
+ * is not searched, which may produce false negatives and duplicate values
+ * (in the unsorted part only), but when we're building the range that's
+ * fine - we'll deduplicate before serialization, and it can only happen
+ * if there already are unsorted values (so it was already modified).
+ *
+ * Serialized ranges don't have any unsorted values, so this can't cause
+ * false negatives during querying.
+ */
+static bool
+range_contains_value(BrinDesc *bdesc, Oid colloid,
+					 AttrNumber attno, Form_pg_attribute attr,
+					 Ranges *ranges, Datum newval, bool full)
+{
+	int			i;
+	FmgrInfo   *cmpEqualFn;
+	Oid			typid = attr->atttypid;
+
+	/*
+	 * First inspect the ranges, if there are any. We first check the whole
+	 * range, and only when there's still a chance of getting a match we
+	 * inspect the individual ranges.
+	 */
+	if (has_matching_range(bdesc, colloid, ranges, newval, attno, typid))
+		return true;
+
+	cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+													BTEqualStrategyNumber);
+
+	/*
+	 * There is no matching range, so let's inspect the sorted values.
+	 *
+	 * We do a sequential search for small number of values, and binary search
+	 * once we have more than 16 values. This threshold is somewhat arbitrary,
+	 * as it depends on how expensive the comparison function is.
+	 *
+	 * XXX If we use the threshold here, maybe we should do the same thing in
+	 * has_matching_range? Or maybe we should do the bin search all the time?
+	 *
+	 * XXX We could use the same optimization as for ranges, to check if the
+	 * value is between min/max, to maybe rule out all sorted values without
+	 * having to inspect all of them.
+	 */
+	if (ranges->nsorted >= 16)
+	{
+		compare_context cxt;
+
+		cxt.colloid = ranges->colloid;
+		cxt.cmpFn = ranges->cmp;
+
+		if (bsearch_arg(&newval, &ranges->values[2 * ranges->nranges],
+						ranges->nsorted, sizeof(Datum),
+						compare_values, (void *) &cxt) != NULL)
+			return true;
+	}
+	else
+	{
+		for (i = 2 * ranges->nranges; i < 2 * ranges->nranges + ranges->nsorted; i++)
+		{
+			Datum		compar;
+
+			compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]);
+
+			/* found an exact match */
+			if (DatumGetBool(compar))
+				return true;
+		}
+	}
+
+	/* If not asked to inspect the unsorted part, we're done. */
+	if (!full)
+		return false;
+
+	/* Inspect the unsorted part. */
+	for (i = 2 * ranges->nranges + ranges->nsorted; i < 2 * ranges->nranges + ranges->nvalues; i++)
+	{
+		Datum		compar;
+
+		compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]);
+
+		/* found an exact match */
+		if (DatumGetBool(compar))
+			return true;
+	}
+
+	/* the value is not covered by this BRIN tuple */
+	return false;
+}
+
+/*
+ * Expand ranges from Ranges into ExpandedRange array. This expects the
+ * eranges to be pre-allocated and with the correct size - there needs to be
+ * (nranges + nvalues) elements.
+ *
+ * The order of expanded ranges is arbitrary. We do expand the ranges first,
+ * and this part is sorted. But then we expand the values, and this part may
+ * be unsorted.
+ */
+static void
+fill_expanded_ranges(ExpandedRange *eranges, int neranges, Ranges *ranges)
+{
+	int			idx;
+	int			i;
+
+	/* Check that the output array has the right size. */
+	Assert(neranges == (ranges->nranges + ranges->nvalues));
+
+	idx = 0;
+	for (i = 0; i < ranges->nranges; i++)
+	{
+		eranges[idx].minval = ranges->values[2 * i];
+		eranges[idx].maxval = ranges->values[2 * i + 1];
+		eranges[idx].collapsed = false;
+		idx++;
+
+		Assert(idx <= neranges);
+	}
+
+	for (i = 0; i < ranges->nvalues; i++)
+	{
+		eranges[idx].minval = ranges->values[2 * ranges->nranges + i];
+		eranges[idx].maxval = ranges->values[2 * ranges->nranges + i];
+		eranges[idx].collapsed = true;
+		idx++;
+
+		Assert(idx <= neranges);
+	}
+
+	/* Did we produce the expected number of elements? */
+	Assert(idx == neranges);
+
+	return;
+}
+
+/*
+ * Sort and deduplicate expanded ranges.
+ *
+ * The ranges may be deduplicated - we're simply appending values, without
+ * checking for duplicates etc. So maybe the deduplication will reduce the
+ * number of ranges enough, and we won't have to compute the distances etc.
+ *
+ * Returns the number of expanded ranges.
+ */
+static int
+sort_expanded_ranges(FmgrInfo *cmp, Oid colloid,
+					 ExpandedRange *eranges, int neranges)
+{
+	int			n;
+	int			i;
+	compare_context cxt;
+
+	Assert(neranges > 0);
+
+	/* sort the values */
+	cxt.colloid = colloid;
+	cxt.cmpFn = cmp;
+
+	/*
+	 * XXX We do qsort on all the values, but we could also leverage the fact
+	 * that some of the input data is already sorted (all the ranges and maybe
+	 * some of the points) and do merge sort.
+	 */
+	qsort_arg(eranges, neranges, sizeof(ExpandedRange),
+			  compare_expanded_ranges, (void *) &cxt);
+
+	/*
+	 * Deduplicate the ranges - simply compare each range to the preceding
+	 * one, and skip the duplicate ones.
+	 */
+	n = 1;
+	for (i = 1; i < neranges; i++)
+	{
+		/* if the current range is equal to the preceding one, do nothing */
+		if (!compare_expanded_ranges(&eranges[i - 1], &eranges[i], (void *) &cxt))
+			continue;
+
+		/* otherwise copy it to n-th place (if not already there) */
+		if (i != n)
+			memcpy(&eranges[n], &eranges[i], sizeof(ExpandedRange));
+
+		n++;
+	}
+
+	Assert((n > 0) && (n <= neranges));
+
+	return n;
+}
+
+/*
+ * 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 expanded ranges were previously sorted (by minval
+ * and then maxval). We leverage this when detecting overlap.
+ */
+static int
+merge_overlapping_ranges(FmgrInfo *cmp, Oid colloid,
+						 ExpandedRange *eranges, int neranges)
+{
+	int			idx;
+
+	/* Merge ranges (idx) and (idx+1) if they overlap. */
+	idx = 0;
+	while (idx < (neranges - 1))
+	{
+		Datum		r;
+
+		/*
+		 * comparing [?,maxval] vs. [minval,?] - the ranges overlap if (minval
+		 * < maxval)
+		 */
+		r = FunctionCall2Coll(cmp, colloid,
+							  eranges[idx].maxval,
+							  eranges[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 overlap only partially.
+		 * So compare the upper bounds and keep the larger one.
+		 */
+		r = FunctionCall2Coll(cmp, colloid,
+							  eranges[idx].maxval,
+							  eranges[idx + 1].maxval);
+
+		if (DatumGetBool(r))
+			eranges[idx].maxval = eranges[idx + 1].maxval;
+
+		/*
+		 * The range certainly is no longer collapsed (irrespectively of the
+		 * previous state).
+		 */
+		eranges[idx].collapsed = false;
+
+		/*
+		 * Now get rid of the (idx+1) range entirely by shifting the remaining
+		 * ranges by 1. There are neranges elements, and we need to move
+		 * elements from (idx+2). That means the number of elements to move is
+		 * [ncranges - (idx+2)].
+		 */
+		memmove(&eranges[idx + 1], &eranges[idx + 2],
+				(neranges - (idx + 2)) * sizeof(ExpandedRange));
+
+		/*
+		 * Decrease the number of ranges, and repeat (with the same range, as
+		 * it might overlap with additional ranges thanks to the merge).
+		 */
+		neranges--;
+	}
+
+	return neranges;
+}
+
+/*
+ * Simple comparator for distance values, comparing the double value.
+ * This is intentionally sorting the distances in descending order, i.e.
+ * the longer gaps will be at the front.
+ */
+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 expanded 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 (max-min) for pairs
+ * of consecutive ranges. 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_expanded_ranges for details.
+ */
+static DistanceValue *
+build_distances(FmgrInfo *distanceFn, Oid colloid,
+				ExpandedRange *eranges, int neranges)
+{
+	int			i;
+	int			ndistances;
+	DistanceValue *distances;
+
+	Assert(neranges >= 2);
+
+	ndistances = (neranges - 1);
+	distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * ndistances);
+
+	/*
+	 * Walk though the ranges once and compute distance between the ranges so
+	 * that we can sort them once.
+	 */
+	for (i = 0; i < ndistances; i++)
+	{
+		Datum		a1,
+					a2,
+					r;
+
+		a1 = eranges[i].maxval;
+		a2 = eranges[i + 1].minval;
+
+		/* compute length of the gap (between max/min) */
+		r = FunctionCall2Coll(distanceFn, colloid, a1, a2);
+
+		/* remember the index of the gap the distance is for */
+		distances[i].index = i;
+		distances[i].value = DatumGetFloat8(r);
+	}
+
+	/*
+	 * Sort the distances in descending order, so that the longest gaps are at
+	 * the front.
+	 */
+	pg_qsort(distances, ndistances, sizeof(DistanceValue), compare_distances);
+
+	return distances;
+}
+
+/*
+ * Builds expanded ranges for the existing ranges (and single-point ranges),
+ * and also the new value (which did not fit into the array).  This expanded
+ * representation makes the processing a bit easier, as it allows handling
+ * ranges and points the same way.
+ *
+ * We sort and deduplicate the expanded ranges - this is necessary, because
+ * the points may be unsorted. And moreover the two parts (ranges and
+ * points) are sorted on their own.
+ */
+static ExpandedRange *
+build_expanded_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
+					  int *nranges)
+{
+	int			neranges;
+	ExpandedRange *eranges;
+
+	/* both ranges and points are expanded into a separate element */
+	neranges = ranges->nranges + ranges->nvalues;
+
+	eranges = (ExpandedRange *) palloc0(neranges * sizeof(ExpandedRange));
+
+	/* fill the expanded ranges */
+	fill_expanded_ranges(eranges, neranges, ranges);
+
+	/* sort and deduplicate the expanded ranges */
+	neranges = sort_expanded_ranges(cmp, colloid, eranges, neranges);
+
+	/* remember how many cranges we built */
+	*nranges = neranges;
+
+	return eranges;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * Counts boundary 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(ExpandedRange *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;
+}
+#endif
+
+/*
+ * reduce_expanded_ranges
+ *		reduce the ranges until the number of values is low enough
+ *
+ * Combines ranges until the number of boundary values drops below the
+ * threshold specified by max_values. This happens by merging enough
+ * ranges by distance between them.
+ *
+ * Returns the number of result ranges.
+ *
+ * We simply use the global min/max and then add boundaries for enough
+ * largest gaps. Each gap adds 2 values, so we simply use (target/2-1)
+ * distances. Then we simply sort all the values - each two values are
+ * a boundary of a range (possibly collapsed).
+ *
+ * XXX Some of the ranges may be collapsed (i.e. the min/max values are
+ * equal), but we ignore that for now. We could repeat the process,
+ * adding a couple more gaps recursively.
+ *
+ * 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).
+ *
+ * XXX Or maybe we could just handle this by using random value as a
+ * tie-break, or by adding random noise to the actual distance.
+ */
+static int
+reduce_expanded_ranges(ExpandedRange *eranges, int neranges,
+					   DistanceValue *distances, int max_values,
+					   FmgrInfo *cmp, Oid colloid)
+{
+	int			i;
+	int			nvalues;
+	Datum	   *values;
+
+	compare_context cxt;
+
+	/* total number of gaps between ranges */
+	int			ndistances = (neranges - 1);
+
+	/* number of gaps to keep */
+	int			keep = (max_values / 2 - 1);
+
+	/*
+	 * Maybe we have sufficiently low number of ranges already?
+	 *
+	 * XXX This should happen before we actually do the expensive stuff like
+	 * sorting, so maybe this should be just an assert.
+	 */
+	if (keep >= ndistances)
+		return neranges;
+
+	/* sort the values */
+	cxt.colloid = colloid;
+	cxt.cmpFn = cmp;
+
+	/* allocate space for the boundary values */
+	nvalues = 0;
+	values = (Datum *) palloc(sizeof(Datum) * max_values);
+
+	/* add the global min/max values, from the first/last range */
+	values[nvalues++] = eranges[0].minval;
+	values[nvalues++] = eranges[neranges - 1].maxval;
+
+	/* add boundary values for enough gaps */
+	for (i = 0; i < keep; i++)
+	{
+		/* index of the gap between (index) and (index+1) ranges */
+		int			index = distances[i].index;
+
+		Assert((index >= 0) && ((index + 1) < neranges));
+
+		/* add max from the preceding range, minval from the next one */
+		values[nvalues++] = eranges[index].maxval;
+		values[nvalues++] = eranges[index + 1].minval;
+
+		Assert(nvalues <= max_values);
+	}
+
+	/* We should have even number of range values. */
+	Assert(nvalues % 2 == 0);
+
+	/*
+	 * Sort the values using the comparator function, and form ranges from the
+	 * sorted result.
+	 */
+	qsort_arg(values, nvalues, sizeof(Datum),
+			  compare_values, (void *) &cxt);
+
+	/* We have nvalues boundary values, which means nvalues/2 ranges. */
+	for (i = 0; i < (nvalues / 2); i++)
+	{
+		eranges[i].minval = values[2 * i];
+		eranges[i].maxval = values[2 * i + 1];
+
+		/* if the boundary values are the same, it's a collapsed range */
+		eranges[i].collapsed = (compare_values(&values[2 * i],
+											   &values[2 * i + 1],
+											   &cxt) == 0);
+	}
+
+	return (nvalues / 2);
+}
+
+/*
+ * Store the boundary values from ExpandedRanges back into Range (using
+ * only the minimal number of values needed).
+ */
+static void
+store_expanded_ranges(Ranges *ranges, ExpandedRange *eranges, int neranges)
+{
+	int			i;
+	int			idx = 0;
+
+	/* first copy in the regular ranges */
+	ranges->nranges = 0;
+	for (i = 0; i < neranges; i++)
+	{
+		if (!eranges[i].collapsed)
+		{
+			ranges->values[idx++] = eranges[i].minval;
+			ranges->values[idx++] = eranges[i].maxval;
+			ranges->nranges++;
+		}
+	}
+
+	/* now copy in the collapsed ones */
+	ranges->nvalues = 0;
+	for (i = 0; i < neranges; i++)
+	{
+		if (eranges[i].collapsed)
+		{
+			ranges->values[idx++] = eranges[i].minval;
+			ranges->nvalues++;
+		}
+	}
+
+	/* all the values are sorted */
+	ranges->nsorted = ranges->nvalues;
+
+	Assert(count_values(eranges, neranges) == 2 * ranges->nranges + ranges->nvalues);
+	Assert(2 * ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+}
+
+
+/*
+ * Consider freeing space in the ranges. Checks if there's space for at least
+ * one new value, and performs compaction if needed.
+ *
+ * Returns true if the value was actually modified.
+ */
+static bool
+ensure_free_space_in_buffer(BrinDesc *bdesc, Oid colloid,
+							AttrNumber attno, Form_pg_attribute attr,
+							Ranges *range)
+{
+	MemoryContext ctx;
+	MemoryContext oldctx;
+
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* expanded ranges */
+	ExpandedRange *eranges;
+	int			neranges;
+	DistanceValue *distances;
+
+	/*
+	 * If there is free space in the buffer, we're done without having to
+	 * modify anything.
+	 */
+	if (2 * range->nranges + range->nvalues < range->maxvalues)
+		return false;
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+											   BTLessStrategyNumber);
+
+	/* deduplicate values, if there's unsorted part */
+	range_deduplicate_values(range);
+
+	/*
+	 * did we reduce enough free space by just the deduplication?
+	 *
+	 * We don't simply check against range->maxvalues again. The deduplication
+	 * might have freed very little space (e.g. just one value), forcing us to
+	 * do depuplication very often. In that case it's better to do compaction
+	 * and reduce more space.
+	 */
+	if (2 * range->nranges + range->nvalues <= range->maxvalues * MINMAX_BUFFER_LOAD_FACTOR)
+		return true;
+
+	/*
+	 * We need to combine some of the existing ranges, to reduce the number of
+	 * values we have to store.
+	 *
+	 * The distanceFn calls (which may internally call e.g. numeric_le) may
+	 * allocate quite a bit of memory, and we must not leak it (we might have
+	 * to do this repeatedly, even for a single BRIN page range). Otherwise
+	 * we'd have problems e.g. when building new indexes. So we use a memory
+	 * context and make sure we free the memory at the end (so if we call the
+	 * distance function many times, it might be an issue, but meh).
+	 */
+	ctx = AllocSetContextCreate(CurrentMemoryContext,
+								"minmax-multi context",
+								ALLOCSET_DEFAULT_SIZES);
+
+	oldctx = MemoryContextSwitchTo(ctx);
+
+	/* build the expanded ranges */
+	eranges = build_expanded_ranges(cmpFn, colloid, range, &neranges);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE);
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, colloid, eranges, neranges);
+
+	/*
+	 * Combine ranges until we release at least 50% of the space. This
+	 * threshold is somewhat arbitrary, perhaps needs tuning. We must not use
+	 * too low or high value.
+	 */
+	neranges = reduce_expanded_ranges(eranges, neranges, distances,
+									  range->maxvalues * MINMAX_BUFFER_LOAD_FACTOR,
+									  cmpFn, colloid);
+
+	/* Make sure we've sufficiently reduced the number of ranges. */
+	Assert(count_values(eranges, neranges) <= range->maxvalues * MINMAX_BUFFER_LOAD_FACTOR);
+
+	/* decompose the expanded ranges into regular ranges and single values */
+	store_expanded_ranges(range, eranges, neranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* Did we break the ranges somehow? */
+	AssertCheckRanges(range, cmpFn, colloid);
+
+	return true;
+}
+
+/*
+ * range_add_value
+ * 		Add the new value to the minmax-multi range.
+ */
+static bool
+range_add_value(BrinDesc *bdesc, Oid colloid,
+				AttrNumber attno, Form_pg_attribute attr,
+				Ranges *ranges, Datum newval)
+{
+	FmgrInfo   *cmpFn;
+	bool		modified = false;
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+											   BTLessStrategyNumber);
+
+	/* comprehensive checks of the input ranges */
+	AssertCheckRanges(ranges, cmpFn, colloid);
+
+	/*
+	 * Make sure there's enough free space in the buffer. We only trigger this
+	 * when the buffer is full, which means it had to be modified as we size
+	 * it to be larger than what is stored on disk.
+	 *
+	 * This needs to happen before we check if the value is contained in the
+	 * range, because the value might be in the unsorted part, and we don't
+	 * check that in range_contains_value. The deduplication would then move
+	 * it to the sorted part, and we'd add the value too, which violates the
+	 * rule that we never have duplicates with the ranges or sorted values.
+	 *
+	 * We might also deduplicate and recheck if the value is contained, but
+	 * that seems like an overkill. We'd need to deduplicate anyway, so why
+	 * not do it now.
+	 */
+	modified = ensure_free_space_in_buffer(bdesc, colloid,
+										   attno, attr, ranges);
+
+	/*
+	 * 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 the values array can't contain duplicate values.
+	 */
+	if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval, false))
+		return modified;
+
+	/* Make a copy of the value, if needed. */
+	newval = datumCopy(newval, attr->attbyval, attr->attlen);
+
+	/*
+	 * 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.
+	 */
+	ranges->values[2 * ranges->nranges + ranges->nvalues] = newval;
+	ranges->nvalues++;
+
+	/* If we added the first value, we can consider it as sorted. */
+	if (ranges->nvalues == 1)
+		ranges->nsorted = 1;
+
+	/*
+	 * Check we haven't broken the ordering of boundary values (checks both
+	 * parts, but that doesn't hurt).
+	 */
+	AssertCheckRanges(ranges, cmpFn, colloid);
+
+	/* Check the range contains the value we just added. */
+	Assert(range_contains_value(bdesc, colloid, attno, attr, ranges, newval, true));
+
+	/* yep, we've modified the range */
+	return true;
+}
+
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_expanded_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+	FmgrInfo   *cmpFn,
+			   *distanceFn;
+
+	/* expanded ranges */
+	ExpandedRange *eranges;
+	int			neranges;
+	DistanceValue *distances;
+
+	MemoryContext ctx;
+	MemoryContext oldctx;
+
+	/*
+	 * Do we need to actually compactify anything?
+	 *
+	 * There are two reasons why compaction may be needed - firstly, there may
+	 * be too many values, or some of the values may be unsorted.
+	 */
+	if ((ranges->nranges * 2 + ranges->nvalues <= max_values) &&
+		(ranges->nsorted == ranges->nvalues))
+		return;
+
+	/* we'll certainly need the comparator, so just look it up now */
+	cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+											   BTLessStrategyNumber);
+
+	/* and we'll also need the 'distance' procedure */
+	distanceFn = minmax_multi_get_procinfo(bdesc, ranges->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);
+
+	/* build the expanded ranges */
+	eranges = build_expanded_ranges(cmpFn, ranges->colloid, ranges, &neranges);
+
+	/* build array of gap distances and sort them in ascending order */
+	distances = build_distances(distanceFn, ranges->colloid,
+								eranges, neranges);
+
+	/*
+	 * Combine ranges until we get below max_values. We don't use any scale
+	 * factor, because this is used during serialization, and we don't expect
+	 * more tuples to be inserted anytime soon.
+	 */
+	neranges = reduce_expanded_ranges(eranges, neranges, distances,
+									  max_values, cmpFn, ranges->colloid);
+
+	Assert(count_values(eranges, neranges) <= max_values);
+
+	/* transform back into regular ranges and single values */
+	store_expanded_ranges(ranges, eranges, neranges);
+
+	/* check all the range invariants */
+	AssertCheckRanges(ranges, cmpFn, ranges->colloid);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+}
+
+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(PG_BRIN_MINMAX_MULTI_SUMMARYOID, 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);
+
+	/*
+	 * We use the no-check variants here, because user-supplied values may
+	 * have (ip_posid == 0). See ItemPointerCompare.
+	 */
+	da1 = ItemPointerGetBlockNumberNoCheck(pa1) * MaxHeapTuplesPerPage +
+		ItemPointerGetOffsetNumberNoCheck(pa1);
+
+	da2 = ItemPointerGetBlockNumberNoCheck(pa2) * MaxHeapTuplesPerPage +
+		ItemPointerGetOffsetNumberNoCheck(pa2);
+
+	PG_RETURN_FLOAT8(da2 - da1);
+}
+
+/*
+ * Computes 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;
+	float8		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);
+}
+
+/*
+ * Compute approximate distance between two dates.
+ */
+Datum
+brin_minmax_multi_distance_date(PG_FUNCTION_ARGS)
+{
+	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
+	DateADT		dateVal2 = PG_GETARG_DATEADT(1);
+
+	if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
+		PG_RETURN_FLOAT8(0);
+
+	PG_RETURN_FLOAT8(dateVal1 - dateVal2);
+}
+
+/*
+ * 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)
+{
+	float8		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.
+ */
+Datum
+brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS)
+{
+	float8		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);
+}
+
+Datum
+brin_minmax_multi_distance_timestamp(PG_FUNCTION_ARGS)
+{
+	float8		delta = 0;
+
+	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
+	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
+
+	if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2))
+		PG_RETURN_FLOAT8(0);
+
+	delta = dt2 - dt1;
+
+	Assert(delta >= 0);
+
+	PG_RETURN_FLOAT8(delta);
+}
+
+/*
+ * Computes distance between two interval values.
+ */
+Datum
+brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS)
+{
+	float8		delta = 0;
+
+	Interval   *ia = PG_GETARG_INTERVAL_P(0);
+	Interval   *ib = PG_GETARG_INTERVAL_P(1);
+	Interval   *result;
+
+	result = (Interval *) palloc(sizeof(Interval));
+
+	result->month = ib->month - ia->month;
+	/* overflow check copied from int4mi */
+	if (!SAMESIGN(ib->month, ia->month) &&
+		!SAMESIGN(result->month, ib->month))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("interval out of range")));
+
+	result->day = ib->day - ia->day;
+	if (!SAMESIGN(ib->day, ia->day) &&
+		!SAMESIGN(result->day, ib->day))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("interval out of range")));
+
+	result->time = ib->time - ia->time;
+	if (!SAMESIGN(ib->time, ia->time) &&
+		!SAMESIGN(result->time, ib->time))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("interval out of range")));
+
+	/*
+	 * We assume months have 31 days - we don't need to be precise, in the
+	 * worst case we'll build somewhat less efficient ranges.
+	 */
+	delta = (float8) (result->month * 31 + result->day);
+
+	/* convert to microseconds (just like the time part) */
+	delta = 24L * 3600L * delta;
+
+	/* and add the time part */
+	delta += result->time / (float8) 1000000.0;
+
+	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)
+{
+	float8		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)
+{
+	float8		delta;
+
+	macaddr    *a = PG_GETARG_MACADDR_P(0);
+	macaddr    *b = PG_GETARG_MACADDR_P(1);
+
+	delta = ((float8) b->f - (float8) a->f);
+	delta /= 256;
+
+	delta += ((float8) b->e - (float8) a->e);
+	delta /= 256;
+
+	delta += ((float8) b->d - (float8) a->d);
+	delta /= 256;
+
+	delta += ((float8) b->c - (float8) a->c);
+	delta /= 256;
+
+	delta += ((float8) b->b - (float8) a->b);
+	delta /= 256;
+
+	delta += ((float8) b->a - (float8) 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)
+{
+	float8		delta;
+
+	macaddr8   *a = PG_GETARG_MACADDR8_P(0);
+	macaddr8   *b = PG_GETARG_MACADDR8_P(1);
+
+	delta = ((float8) b->h - (float8) a->h);
+	delta /= 256;
+
+	delta += ((float8) b->g - (float8) a->g);
+	delta /= 256;
+
+	delta += ((float8) b->f - (float8) a->f);
+	delta /= 256;
+
+	delta += ((float8) b->e - (float8) a->e);
+	delta /= 256;
+
+	delta += ((float8) b->d - (float8) a->d);
+	delta /= 256;
+
+	delta += ((float8) b->c - (float8) a->c);
+	delta /= 256;
+
+	delta += ((float8) b->b - (float8) a->b);
+	delta /= 256;
+
+	delta += ((float8) b->a - (float8) 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 different families are considered 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)
+{
+	float8		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))
+		PG_RETURN_FLOAT8(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 += (float8) addrb[i] - (float8) addra[i];
+		delta /= 256;
+	}
+
+	Assert((delta >= 0) && (delta <= 1));
+
+	PG_RETURN_FLOAT8(delta);
+}
+
+static void
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
+{
+	Ranges	   *ranges = (Ranges *) DatumGetPointer(src);
+	SerializedRanges *s;
+
+	/*
+	 * In batch mode, we need to compress the accumulated values to the
+	 * actually requested number of values/ranges.
+	 */
+	compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+	/* At this point everything has to be fully sorted. */
+	Assert(ranges->nsorted == ranges->nvalues);
+
+	s = range_serialize(ranges);
+	dst[0] = PointerGetDatum(s);
+}
+
+static int
+brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxMultiOptions *opts)
+{
+	return MinMaxMultiGetValuesPerRange(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);
+	MinMaxMultiOptions *opts = (MinMaxMultiOptions *) PG_GET_OPCLASS_OPTIONS();
+	Oid			colloid = PG_GET_COLLATION();
+	bool		modified = false;
+	Form_pg_attribute attr;
+	AttrNumber	attno;
+	Ranges	   *ranges;
+	SerializedRanges *serialized = NULL;
+
+	Assert(!isnull);
+
+	attno = column->bv_attno;
+	attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+
+	/* use the already deserialized value, if possible */
+	ranges = (Ranges *) DatumGetPointer(column->bv_mem_value);
+
+	/*
+	 * If this is the first non-null value, we need to initialize the range
+	 * list. Otherwise just extract the existing range list from BrinValues.
+	 *
+	 * When starting with an empty range, we assume this is a batch mode and
+	 * we use a larger buffer. The buffer size is derived from the BRIN range
+	 * size, number of rows per page, with some sensible min/max values. Small
+	 * buffer would be bad for performance, but large buffer might require a
+	 * lot of memory (because of keeping all the values).
+	 */
+	if (column->bv_allnulls)
+	{
+		MemoryContext oldctx;
+
+		int			target_maxvalues;
+		int			maxvalues;
+		BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
+
+		/* what was specified as a reloption? */
+		target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
+
+		/*
+		 * Determine the insert buffer size - we use 10x the target, capped to
+		 * the maximum number of values in the heap range. This is more than
+		 * enough, considering the actual number of rows per page is likely
+		 * much lower, but meh.
+		 */
+		maxvalues = Min(target_maxvalues * MINMAX_BUFFER_FACTOR,
+						MaxHeapTuplesPerPage * pagesPerRange);
+
+		/* but always at least the original value */
+		maxvalues = Max(maxvalues, target_maxvalues);
+
+		/* always cap by MIN/MAX */
+		maxvalues = Max(maxvalues, MINMAX_BUFFER_MIN);
+		maxvalues = Min(maxvalues, MINMAX_BUFFER_MAX);
+
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+		ranges = minmax_multi_init(maxvalues);
+		ranges->attno = attno;
+		ranges->colloid = colloid;
+		ranges->typid = attr->atttypid;
+		ranges->target_maxvalues = target_maxvalues;
+
+		/* we'll certainly need the comparator, so just look it up now */
+		ranges->cmp = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+														 BTLessStrategyNumber);
+
+		MemoryContextSwitchTo(oldctx);
+
+		column->bv_allnulls = false;
+		modified = true;
+
+		column->bv_mem_value = PointerGetDatum(ranges);
+		column->bv_serialize = brin_minmax_multi_serialize;
+	}
+	else if (!ranges)
+	{
+		MemoryContext oldctx;
+
+		int			maxvalues;
+		BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
+
+		oldctx = MemoryContextSwitchTo(column->bv_context);
+
+		serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+
+		/*
+		 * Determine the insert buffer size - we use 10x the target, capped to
+		 * the maximum number of values in the heap range. This is more than
+		 * enough, considering the actual number of rows per page is likely
+		 * much lower, but meh.
+		 */
+		maxvalues = Min(serialized->maxvalues * MINMAX_BUFFER_FACTOR,
+						MaxHeapTuplesPerPage * pagesPerRange);
+
+		/* but always at least the original value */
+		maxvalues = Max(maxvalues, serialized->maxvalues);
+
+		/* always cap by MIN/MAX */
+		maxvalues = Max(maxvalues, MINMAX_BUFFER_MIN);
+		maxvalues = Min(maxvalues, MINMAX_BUFFER_MAX);
+
+		ranges = range_deserialize(maxvalues, serialized);
+
+		ranges->attno = attno;
+		ranges->colloid = colloid;
+		ranges->typid = attr->atttypid;
+
+		/* we'll certainly need the comparator, so just look it up now */
+		ranges->cmp = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+														 BTLessStrategyNumber);
+
+		column->bv_mem_value = PointerGetDatum(ranges);
+		column->bv_serialize = brin_minmax_multi_serialize;
+
+		MemoryContextSwitchTo(oldctx);
+	}
+
+	/*
+	 * Try to add the new value to the range. We need to update the modified
+	 * flag, so that we serialize the updated summary later.
+	 */
+	modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval);
+
+
+	PG_RETURN_BOOL(modified);
+}
+
+/*
+ * Given an index tuple corresponding to a certain page range and a scan key,
+ * return whether the scan key is consistent with the index tuple's min/max
+ * values.  Return true if so, false otherwise.
+ */
+Datum
+brin_minmax_multi_consistent(PG_FUNCTION_ARGS)
+{
+	BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+	BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
+	ScanKey    *keys = (ScanKey *) PG_GETARG_POINTER(2);
+	int			nkeys = PG_GETARG_INT32(3);
+
+	Oid			colloid = PG_GET_COLLATION(),
+				subtype;
+	AttrNumber	attno;
+	Datum		value;
+	FmgrInfo   *finfo;
+	SerializedRanges *serialized;
+	Ranges	   *ranges;
+	int			keyno;
+	int			rangeno;
+	int			i;
+
+	attno = column->bv_attno;
+
+	serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+	ranges = range_deserialize(serialized->maxvalues, serialized);
+
+	/* inspect the ranges, and for each one evaluate the scan keys */
+	for (rangeno = 0; rangeno < ranges->nranges; rangeno++)
+	{
+		Datum		minval = ranges->values[2 * rangeno];
+		Datum		maxval = ranges->values[2 * rangeno + 1];
+
+		/* assume the range is matching, and we'll try to prove otherwise */
+		bool		matching = true;
+
+		for (keyno = 0; keyno < nkeys; keyno++)
+		{
+			Datum		matches;
+			ScanKey		key = keys[keyno];
+
+			/* NULL keys are handled and filtered-out in bringetbitmap */
+			Assert(!(key->sk_flags & SK_ISNULL));
+
+			attno = key->sk_attno;
+			subtype = key->sk_subtype;
+			value = key->sk_argument;
+			switch (key->sk_strategy)
+			{
+				case BTLessStrategyNumber:
+				case BTLessEqualStrategyNumber:
+					finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+															   key->sk_strategy);
+					/* first value from the array */
+					matches = FunctionCall2Coll(finfo, colloid, minval, value);
+					break;
+
+				case BTEqualStrategyNumber:
+					{
+						Datum		compar;
+						FmgrInfo   *cmpFn;
+
+						/* by default this range does not match */
+						matches = false;
+
+						/*
+						 * Otherwise, need to compare the new value with
+						 * boundaries of all the ranges. First check if it's
+						 * less than the absolute minimum, which is the first
+						 * value in the array.
+						 */
+						cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+																   BTLessStrategyNumber);
+						compar = FunctionCall2Coll(cmpFn, colloid, value, minval);
+
+						/* smaller than the smallest value in this range */
+						if (DatumGetBool(compar))
+							break;
+
+						cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+																   BTGreaterStrategyNumber);
+						compar = FunctionCall2Coll(cmpFn, colloid, value, maxval);
+
+						/* larger than the largest value in this range */
+						if (DatumGetBool(compar))
+							break;
+
+						/*
+						 * haven't managed to eliminate this range, so
+						 * consider it matching
+						 */
+						matches = true;
+
+						break;
+					}
+				case BTGreaterEqualStrategyNumber:
+				case BTGreaterStrategyNumber:
+					finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+															   key->sk_strategy);
+					/* last value from the array */
+					matches = FunctionCall2Coll(finfo, colloid, maxval, value);
+					break;
+
+				default:
+					/* shouldn't happen */
+					elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+					matches = 0;
+					break;
+			}
+
+			/* the range has to match all the scan keys */
+			matching &= DatumGetBool(matches);
+
+			/* once we find a non-matching key, we're done */
+			if (!matching)
+				break;
+		}
+
+		/*
+		 * have we found a range matching all scan keys? if yes, we're done
+		 */
+		if (matching)
+			PG_RETURN_DATUM(BoolGetDatum(true));
+	}
+
+	/*
+	 * And now inspect the values. We don't bother with doing a binary search
+	 * here, because we're dealing with serialized / fully compacted ranges,
+	 * so there should be only very few 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);
+
+	Oid			colloid = PG_GET_COLLATION();
+	SerializedRanges *serialized_a;
+	SerializedRanges *serialized_b;
+	Ranges	   *ranges_a;
+	Ranges	   *ranges_b;
+	AttrNumber	attno;
+	Form_pg_attribute attr;
+	ExpandedRange *eranges;
+	int			neranges;
+	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->maxvalues, serialized_a);
+	ranges_b = range_deserialize(serialized_b->maxvalues, serialized_b);
+
+	/* make sure neither of the ranges is NULL */
+	Assert(ranges_a && ranges_b);
+
+	neranges = (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 */
+	eranges = (ExpandedRange *) palloc0(neranges * sizeof(ExpandedRange));
+
+	/* fill the expanded ranges with entries for the first range */
+	fill_expanded_ranges(eranges, ranges_a->nranges + ranges_a->nvalues,
+						 ranges_a);
+
+	/* and now add combine ranges for the second range */
+	fill_expanded_ranges(&eranges[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 expanded ranges */
+	sort_expanded_ranges(cmpFn, colloid, eranges, neranges);
+
+	/*
+	 * We've loaded two different lists of expanded ranges, so some of them
+	 * may be overlapping. So walk through them and merge them.
+	 */
+	neranges = merge_overlapping_ranges(cmpFn, colloid, eranges, neranges);
+
+	/* check that the combine ranges are correct (no overlaps, ordering) */
+	AssertCheckExpandedRanges(bdesc, colloid, attno, attr, eranges, neranges);
+
+	/*
+	 * If needed, reduce some of the ranges.
+	 *
+	 * XXX This may be fairly expensive, so maybe we should do it only when
+	 * it's actually needed (when we have too many ranges).
+	 */
+
+	/* 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, eranges, neranges);
+
+	/*
+	 * See how many values would be needed to store the current ranges, and if
+	 * needed combine as many of them to get below the threshold. The
+	 * collapsed ranges will be stored as a single value.
+	 *
+	 * XXX This does not apply the load factor, as we don't expect to add more
+	 * values to the range, so we prefer to keep as many ranges as possible.
+	 *
+	 * XXX Can the maxvalues be different in the two ranges? Perhaps we should
+	 * use maximum of those?
+	 */
+	neranges = reduce_expanded_ranges(eranges, neranges, distances,
+									  ranges_a->maxvalues,
+									  cmpFn, colloid);
+
+	/* update the first range summary */
+	store_expanded_ranges(ranges_a, eranges, neranges);
+
+	MemoryContextSwitchTo(oldctx);
+	MemoryContextDelete(ctx);
+
+	/* cleanup and update the serialized value */
+	pfree(serialized_a);
+	col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a));
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Cache and return minmax multi opclass support procedure
+ *
+ * Return the procedure corresponding to the given function support number
+ * or null if it does not exist.
+ */
+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(MinMaxMultiOptions));
+
+	add_local_int_reloption(relopts, "values_per_range", "desc",
+							MINMAX_MULTI_DEFAULT_VALUES_PER_PAGE, 8, 256,
+							offsetof(MinMaxMultiOptions, valuesPerRange));
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * brin_minmax_multi_summary_in
+ *		- input routine for type brin_minmax_multi_summary.
+ *
+ * brin_minmax_multi_summary is only used internally to represent summaries
+ * in BRIN minmax-multi indexes, so it has no operations of its own, and we
+ * disallow input too.
+ */
+Datum
+brin_minmax_multi_summary_in(PG_FUNCTION_ARGS)
+{
+	/*
+	 * brin_minmax_multi_summary stores the data in binary form and parsing
+	 * text input is not needed, so disallow this.
+	 */
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type %s", "brin_minmax_multi_summary")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+
+/*
+ * brin_minmax_multi_summary_out
+ *		- output routine for type brin_minmax_multi_summary.
+ *
+ * BRIN minmax-multi summaries are serialized into a bytea value, but we
+ * want to output something nicer humans can understand.
+ */
+Datum
+brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
+{
+	int			i;
+	int			idx;
+	SerializedRanges *ranges;
+	Ranges	   *ranges_deserialized;
+	StringInfoData str;
+	bool		isvarlena;
+	Oid			outfunc;
+	FmgrInfo	fmgrinfo;
+	ArrayBuildState *astate_values = NULL;
+
+	initStringInfo(&str);
+	appendStringInfoChar(&str, '{');
+
+	/*
+	 * Detoast to get value with full 4B header (can't be stored in a toast
+	 * table, but can use 1B header).
+	 */
+	ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+
+	/* lookup output func for the type */
+	getTypeOutputInfo(ranges->typid, &outfunc, &isvarlena);
+	fmgr_info(outfunc, &fmgrinfo);
+
+	/* deserialize the range info easy-to-process pieces */
+	ranges_deserialized = range_deserialize(ranges->maxvalues, ranges);
+
+	appendStringInfo(&str, "nranges: %u  nvalues: %u  maxvalues: %u",
+					 ranges_deserialized->nranges,
+					 ranges_deserialized->nvalues,
+					 ranges_deserialized->maxvalues);
+
+	/* serialize ranges */
+	idx = 0;
+	for (i = 0; i < ranges_deserialized->nranges; i++)
+	{
+		Datum		a,
+					b;
+		text	   *c;
+		StringInfoData str;
+
+		initStringInfo(&str);
+
+		a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]);
+		b = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]);
+
+		appendStringInfo(&str, "%s ... %s",
+						 DatumGetPointer(a),
+						 DatumGetPointer(b));
+
+		c = cstring_to_text(str.data);
+
+		astate_values = accumArrayResult(astate_values,
+										 PointerGetDatum(c),
+										 false,
+										 TEXTOID,
+										 CurrentMemoryContext);
+	}
+
+	if (ranges_deserialized->nranges > 0)
+	{
+		Oid			typoutput;
+		bool		typIsVarlena;
+		Datum		val;
+		char	   *extval;
+
+		getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
+
+		val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+
+		extval = OidOutputFunctionCall(typoutput, val);
+
+		appendStringInfo(&str, " ranges: %s", extval);
+	}
+
+	/* serialize individual values */
+	astate_values = NULL;
+
+	for (i = 0; i < ranges_deserialized->nvalues; i++)
+	{
+		Datum		a;
+		text	   *b;
+		StringInfoData str;
+
+		initStringInfo(&str);
+
+		a = FunctionCall1(&fmgrinfo, ranges_deserialized->values[idx++]);
+
+		appendStringInfo(&str, "%s", DatumGetPointer(a));
+
+		b = cstring_to_text(str.data);
+
+		astate_values = accumArrayResult(astate_values,
+										 PointerGetDatum(b),
+										 false,
+										 TEXTOID,
+										 CurrentMemoryContext);
+	}
+
+	if (ranges_deserialized->nvalues > 0)
+	{
+		Oid			typoutput;
+		bool		typIsVarlena;
+		Datum		val;
+		char	   *extval;
+
+		getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
+
+		val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+
+		extval = OidOutputFunctionCall(typoutput, val);
+
+		appendStringInfo(&str, " values: %s", extval);
+	}
+
+
+	appendStringInfoChar(&str, '}');
+
+	PG_RETURN_CSTRING(str.data);
+}
+
+/*
+ * brin_minmax_multi_summary_recv
+ *		- binary input routine for type brin_minmax_multi_summary.
+ */
+Datum
+brin_minmax_multi_summary_recv(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type %s", "brin_minmax_multi_summary")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * brin_minmax_multi_summary_send
+ *		- binary output routine for type brin_minmax_multi_summary.
+ *
+ * BRIN minmax-multi summaries are serialized in a bytea value (although
+ * the type is named differently), so let's just send that.
+ */
+Datum
+brin_minmax_multi_summary_send(PG_FUNCTION_ARGS)
+{
+	return byteasend(fcinfo);
+}
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index 8d03e609a3..baf87bdcae 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -159,6 +159,14 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 		if (tuple->bt_columns[keyno].bv_hasnulls)
 			anynulls = true;
 
+		/* If needed, serialize the values before forming the on-disk tuple. */
+		if (tuple->bt_columns[keyno].bv_serialize)
+		{
+			tuple->bt_columns[keyno].bv_serialize(brdesc,
+												  tuple->bt_columns[keyno].bv_mem_value,
+												  tuple->bt_columns[keyno].bv_values);
+		}
+
 		/*
 		 * Now obtain the values of each stored datum.  Note that some values
 		 * might be toasted, and we cannot rely on the original heap values
@@ -193,9 +201,9 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 			 * If value is stored EXTERNAL, must fetch it so we are not
 			 * depending on outside storage.
 			 *
-			 * XXX Is this actually true? Could it be that the summary is
-			 * NULL even for range with non-NULL data? E.g. degenerate bloom
-			 * filter may be thrown away, etc.
+			 * XXX Is this actually true? Could it be that the summary is NULL
+			 * even for range with non-NULL data? E.g. degenerate bloom filter
+			 * may be thrown away, etc.
 			 */
 			if (VARATT_IS_EXTERNAL(DatumGetPointer(value)))
 			{
@@ -510,6 +518,11 @@ brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc)
 		dtuple->bt_columns[i].bv_allnulls = true;
 		dtuple->bt_columns[i].bv_hasnulls = false;
 		dtuple->bt_columns[i].bv_values = (Datum *) currdatum;
+
+		dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL);
+		dtuple->bt_columns[i].bv_serialize = NULL;
+		dtuple->bt_columns[i].bv_context = dtuple->bt_context;
+
 		currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored;
 	}
 
@@ -589,6 +602,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple)
 
 		dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno];
 		dtup->bt_columns[keyno].bv_allnulls = false;
+
+		dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL);
+		dtup->bt_columns[keyno].bv_serialize = NULL;
+		dtup->bt_columns[keyno].bv_context = dtup->bt_context;
 	}
 
 	MemoryContextSwitchTo(oldcxt);
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 5715bd58df..87de94f397 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,6 +14,11 @@
 #include "access/brin_internal.h"
 #include "access/tupdesc.h"
 
+/*
+ * The BRIN opclasses may register serialization callback, in case the on-disk
+ * and in-memory representations differ (e.g. for performance reasons).
+ */
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum *dst);
 
 /*
  * A BRIN index stores one index tuple per page range.  Each index tuple
@@ -27,6 +32,9 @@ typedef struct BrinValues
 	bool		bv_hasnulls;	/* are there any nulls in the page range? */
 	bool		bv_allnulls;	/* are all values nulls in the page range? */
 	Datum	   *bv_values;		/* current accumulated values */
+	Datum		bv_mem_value;	/* expanded accumulated values */
+	MemoryContext	bv_context;
+	brin_serialize_callback_type bv_serialize;
 } BrinValues;
 
 /*
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 82e874130d..2f7338ee82 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -161,18 +161,18 @@ FullTransactionIdAdvance(FullTransactionId *dest)
  *		development purposes (such as in-progress patches and forks);
  *		they should not appear in released versions.
  *
- *		OIDs 10000-11999 are reserved for assignment by genbki.pl, for use
+ *		OIDs 10000-12999 are reserved for assignment by genbki.pl, for use
  *		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
@@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest)
  * ----------
  */
 #define FirstGenbkiObjectId		10000
-#define FirstBootstrapObjectId	12000
+#define FirstBootstrapObjectId	13000
 #define FirstNormalObjectId		16384
 
 /*
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 2f18734235..2db38232fe 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202103232
+#define CATALOG_VERSION_NO	202103233
 
 #endif
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 04d678f96a..8135854163 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -2009,6 +2009,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',
@@ -2062,6 +2208,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)',
@@ -2088,6 +2251,22 @@
 { amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid',
   amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)',
   amopmethod => 'brin' },
+# minmax multi tid
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+  amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)',
+  amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+  amoprighttype => 'tid', amopstrategy => '2', amopopr => '<=(tid,tid)',
+  amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+  amoprighttype => 'tid', amopstrategy => '3', amopopr => '=(tid,tid)',
+  amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+  amoprighttype => 'tid', amopstrategy => '4', amopopr => '>=(tid,tid)',
+  amopmethod => 'brin' },
+{ amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
+  amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)',
+  amopmethod => 'brin' },
 
 # minmax float (float4, float8)
 
@@ -2155,6 +2334,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)',
@@ -2180,6 +2425,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',
@@ -2202,6 +2464,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',
@@ -2224,6 +2503,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)',
@@ -2288,6 +2584,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)',
@@ -2439,6 +2752,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',
@@ -2470,6 +2929,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',
@@ -2492,6 +2968,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)',
@@ -2548,6 +3041,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',
@@ -2570,6 +3080,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)',
@@ -2636,6 +3163,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 2af8af3f4e..f2c0ccfa1f 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -986,6 +986,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',
@@ -1081,6 +1227,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' },
@@ -1127,6 +1290,23 @@
 { amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
   amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' },
 
+# minmax multi tid
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '2',
+  amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '3',
+  amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '5',
+  amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
+  amprocrighttype => 'tid', amprocnum => '11', amproc => 'brin_minmax_multi_distance_tid' },
+
 # minmax float
 { amprocfamily => 'brin/float_minmax_ops', amproclefttype => 'float4',
   amprocrighttype => 'float4', amprocnum => '1',
@@ -1177,6 +1357,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',
@@ -1230,6 +1484,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',
@@ -1264,6 +1538,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',
@@ -1297,6 +1591,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',
@@ -1382,6 +1696,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',
@@ -1507,6 +1840,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_time' },
+{ 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_time' },
+{ 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_time' },
+{ 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_time' },
+{ 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_timestamp' },
+{ 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_timestamp' },
+{ 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_date' },
+{ 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_date' },
+{ 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_date' },
+
 # bloom datetime (date, timestamp, timestamptz)
 { amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
   amprocrighttype => 'timestamp', amprocnum => '1',
@@ -1577,6 +2074,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',
@@ -1611,6 +2128,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',
@@ -1671,6 +2208,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',
@@ -1702,7 +2259,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',
@@ -1759,6 +2337,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 6a5bb58baf..da25befefe 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -284,18 +284,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' },
@@ -307,6 +316,9 @@
   opckeytype => 'text', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'oid_minmax_ops',
   opcfamily => 'brin/oid_minmax_ops', opcintype => 'oid', opckeytype => 'oid' },
+{ opcmethod => 'brin', opcname => 'oid_minmax_multi_ops',
+  opcfamily => 'brin/oid_minmax_multi_ops', opcintype => 'oid',
+  opckeytype => 'oid', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'oid_bloom_ops',
   opcfamily => 'brin/oid_bloom_ops', opcintype => 'oid',
   opckeytype => 'oid', opcdefault => 'f' },
@@ -315,33 +327,51 @@
 { opcmethod => 'brin', opcname => 'tid_bloom_ops',
   opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid',
   opcdefault => 'f'},
+{ opcmethod => 'brin', opcname => 'tid_minmax_multi_ops',
+  opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid',
+  opckeytype => 'tid', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'float4_minmax_ops',
   opcfamily => 'brin/float_minmax_ops', opcintype => 'float4',
   opckeytype => 'float4' },
+{ opcmethod => 'brin', opcname => 'float4_minmax_multi_ops',
+  opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float4',
+  opckeytype => 'float4', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'float4_bloom_ops',
   opcfamily => 'brin/float_bloom_ops', opcintype => 'float4',
   opckeytype => 'float4', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'float8_minmax_ops',
   opcfamily => 'brin/float_minmax_ops', opcintype => 'float8',
   opckeytype => 'float8' },
+{ opcmethod => 'brin', opcname => 'float8_minmax_multi_ops',
+  opcfamily => 'brin/float_minmax_multi_ops', opcintype => 'float8',
+  opckeytype => 'float8', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'float8_bloom_ops',
   opcfamily => 'brin/float_bloom_ops', opcintype => 'float8',
   opckeytype => 'float8', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'macaddr_minmax_ops',
   opcfamily => 'brin/macaddr_minmax_ops', opcintype => 'macaddr',
   opckeytype => 'macaddr' },
+{ opcmethod => 'brin', opcname => 'macaddr_minmax_multi_ops',
+  opcfamily => 'brin/macaddr_minmax_multi_ops', opcintype => 'macaddr',
+  opckeytype => 'macaddr', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'macaddr_bloom_ops',
   opcfamily => 'brin/macaddr_bloom_ops', opcintype => 'macaddr',
   opckeytype => 'macaddr', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'macaddr8_minmax_ops',
   opcfamily => 'brin/macaddr8_minmax_ops', opcintype => 'macaddr8',
   opckeytype => 'macaddr8' },
+{ opcmethod => 'brin', opcname => 'macaddr8_minmax_multi_ops',
+  opcfamily => 'brin/macaddr8_minmax_multi_ops', opcintype => 'macaddr8',
+  opckeytype => 'macaddr8', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'macaddr8_bloom_ops',
   opcfamily => 'brin/macaddr8_bloom_ops', opcintype => 'macaddr8',
   opckeytype => 'macaddr8', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'inet_minmax_ops',
   opcfamily => 'brin/network_minmax_ops', opcintype => 'inet',
   opcdefault => 'f', opckeytype => 'inet' },
+{ opcmethod => 'brin', opcname => 'inet_minmax_multi_ops',
+  opcfamily => 'brin/network_minmax_multi_ops', opcintype => 'inet',
+  opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' },
 { opcmethod => 'brin', opcname => 'inet_bloom_ops',
   opcfamily => 'brin/network_bloom_ops', opcintype => 'inet',
   opcdefault => 'f', opckeytype => 'inet', opcdefault => 'f' },
@@ -357,36 +387,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' },
@@ -398,6 +446,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' },
@@ -407,6 +458,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' },
@@ -416,6 +470,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 7cc3d59a8c..04edca6cb0 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -182,10 +182,14 @@
   opfmethod => 'gin', opfname => 'jsonb_path_ops' },
 { oid => '4054',
   opfmethod => 'brin', opfname => 'integer_minmax_ops' },
+{ oid => '4602',
+  opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' },
 { oid => '4572',
   opfmethod => 'brin', opfname => 'integer_bloom_ops' },
 { oid => '4055',
   opfmethod => 'brin', opfname => 'numeric_minmax_ops' },
+{ oid => '4603',
+  opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' },
 { oid => '4056',
   opfmethod => 'brin', opfname => 'text_minmax_ops' },
 { oid => '4573',
@@ -194,10 +198,14 @@
   opfmethod => 'brin', opfname => 'numeric_bloom_ops' },
 { oid => '4058',
   opfmethod => 'brin', opfname => 'timetz_minmax_ops' },
+{ oid => '4604',
+  opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' },
 { oid => '4575',
   opfmethod => 'brin', opfname => 'timetz_bloom_ops' },
 { oid => '4059',
   opfmethod => 'brin', opfname => 'datetime_minmax_ops' },
+{ oid => '4605',
+  opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' },
 { oid => '4576',
   opfmethod => 'brin', opfname => 'datetime_bloom_ops' },
 { oid => '4062',
@@ -214,26 +222,38 @@
   opfmethod => 'brin', opfname => 'name_bloom_ops' },
 { oid => '4068',
   opfmethod => 'brin', opfname => 'oid_minmax_ops' },
+{ oid => '4606',
+  opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' },
 { oid => '4580',
   opfmethod => 'brin', opfname => 'oid_bloom_ops' },
 { oid => '4069',
   opfmethod => 'brin', opfname => 'tid_minmax_ops' },
 { oid => '4581',
   opfmethod => 'brin', opfname => 'tid_bloom_ops' },
+{ oid => '4607',
+  opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' },
 { oid => '4070',
   opfmethod => 'brin', opfname => 'float_minmax_ops' },
+{ oid => '4608',
+  opfmethod => 'brin', opfname => 'float_minmax_multi_ops' },
 { oid => '4582',
   opfmethod => 'brin', opfname => 'float_bloom_ops' },
 { oid => '4074',
   opfmethod => 'brin', opfname => 'macaddr_minmax_ops' },
+{ oid => '4609',
+  opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' },
 { oid => '4583',
   opfmethod => 'brin', opfname => 'macaddr_bloom_ops' },
 { oid => '4109',
   opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' },
+{ oid => '4610',
+  opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' },
 { oid => '4584',
   opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' },
 { oid => '4075',
   opfmethod => 'brin', opfname => 'network_minmax_ops' },
+{ oid => '4611',
+  opfmethod => 'brin', opfname => 'network_minmax_multi_ops' },
 { oid => '4102',
   opfmethod => 'brin', opfname => 'network_inclusion_ops' },
 { oid => '4585',
@@ -244,10 +264,14 @@
   opfmethod => 'brin', opfname => 'bpchar_bloom_ops' },
 { oid => '4077',
   opfmethod => 'brin', opfname => 'time_minmax_ops' },
+{ oid => '4612',
+  opfmethod => 'brin', opfname => 'time_minmax_multi_ops' },
 { oid => '4587',
   opfmethod => 'brin', opfname => 'time_bloom_ops' },
 { oid => '4078',
   opfmethod => 'brin', opfname => 'interval_minmax_ops' },
+{ oid => '4613',
+  opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' },
 { oid => '4588',
   opfmethod => 'brin', opfname => 'interval_bloom_ops' },
 { oid => '4079',
@@ -256,12 +280,16 @@
   opfmethod => 'brin', opfname => 'varbit_minmax_ops' },
 { oid => '4081',
   opfmethod => 'brin', opfname => 'uuid_minmax_ops' },
+{ oid => '4614',
+  opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' },
 { oid => '4589',
   opfmethod => 'brin', opfname => 'uuid_bloom_ops' },
 { oid => '4103',
   opfmethod => 'brin', opfname => 'range_inclusion_ops' },
 { oid => '4082',
   opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' },
+{ oid => '4615',
+  opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' },
 { oid => '4590',
   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 c89df733f7..183358c1ba 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8221,6 +8221,77 @@
   proname => 'brin_minmax_union', prorettype => 'bool',
   proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' },
 
+# BRIN minmax multi
+{ oid => '4616', descr => 'BRIN multi minmax support',
+  proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' },
+{ oid => '4617', 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 => '4618', descr => 'BRIN multi minmax support',
+  proname => 'brin_minmax_multi_consistent', prorettype => 'bool',
+  proargtypes => 'internal internal internal int4',
+  prosrc => 'brin_minmax_multi_consistent' },
+{ oid => '4619', descr => 'BRIN multi minmax support',
+  proname => 'brin_minmax_multi_union', prorettype => 'bool',
+  proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' },
+{ oid => '4620', descr => 'BRIN multi minmax support',
+  proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f',
+  proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' },
+
+{ oid => '4621', descr => 'BRIN multi minmax int2 distance',
+  proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' },
+{ oid => '4622', descr => 'BRIN multi minmax int4 distance',
+  proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' },
+{ oid => '4623', descr => 'BRIN multi minmax int8 distance',
+  proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' },
+{ oid => '4624', descr => 'BRIN multi minmax float4 distance',
+  proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' },
+{ oid => '4625', descr => 'BRIN multi minmax float8 distance',
+  proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' },
+{ oid => '4626', descr => 'BRIN multi minmax numeric distance',
+  proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' },
+{ oid => '4627', descr => 'BRIN multi minmax tid distance',
+  proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' },
+{ oid => '4628', descr => 'BRIN multi minmax uuid distance',
+  proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' },
+{ oid => '4629', descr => 'BRIN multi minmax date distance',
+  proname => 'brin_minmax_multi_distance_date', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_date' },
+{ oid => '4630', descr => 'BRIN multi minmax time distance',
+  proname => 'brin_minmax_multi_distance_time', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' },
+{ oid => '4631', descr => 'BRIN multi minmax interval distance',
+  proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' },
+{ oid => '4632', descr => 'BRIN multi minmax timetz distance',
+  proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' },
+{ oid => '4633', 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 => '4634', descr => 'BRIN multi minmax macaddr distance',
+  proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' },
+{ oid => '4635', descr => 'BRIN multi minmax macaddr8 distance',
+  proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' },
+{ oid => '4636', descr => 'BRIN multi minmax inet distance',
+  proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' },
+{ oid => '4637', descr => 'BRIN multi minmax timestamp distance',
+  proname => 'brin_minmax_multi_distance_timestamp', prorettype => 'float8',
+  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timestamp' },
+
 # BRIN inclusion
 { oid => '4105', descr => 'BRIN inclusion support',
   proname => 'brin_inclusion_opcinfo', prorettype => 'internal',
@@ -11445,4 +11516,18 @@
   proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea',
   proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' },
 
+{ oid => '4638', descr => 'I/O',
+  proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary',
+  proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' },
+{ oid => '4639', descr => 'I/O',
+  proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring',
+  proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' },
+{ oid => '4640', descr => 'I/O',
+  proname => 'brin_minmax_multi_summary_recv', provolatile => 's',
+  prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal',
+  prosrc => 'brin_minmax_multi_summary_recv' },
+{ oid => '4641', descr => 'I/O',
+  proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea',
+  proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' },
+
 ]
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 2a82a3e544..8c145c00be 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -685,4 +685,10 @@
   typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out',
   typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send',
   typalign => 'i', typstorage => 'x', typcollation => 'default' },
+{ oid => '4601',
+  descr => 'BRIN minmax-multi summary',
+  typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S',
+  typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out',
+  typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send',
+  typalign => 'i', typstorage => 'x', typcollation => 'default' },
 ]
diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out
new file mode 100644
index 0000000000..0a7e4b8060
--- /dev/null
+++ b/src/test/regress/expected/brin_multi.out
@@ -0,0 +1,450 @@
+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 = 7)
+);
+ERROR:  value 7 out of bounds for option "values_per_range"
+DETAIL:  Valid values are between "8" 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 "8" and "256".
+-- first create an index with a single page range, to force compaction
+-- due to exceeding the number of values per summary
+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
+);
+DROP INDEX brinidx_multi;
+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 building an index with many values, to force compaction of the buffer
+CREATE TABLE brin_large_range (a int4);
+INSERT INTO brin_large_range SELECT i FROM generate_series(1,10000) s(i);
+CREATE INDEX brin_large_range_idx ON brin_large_range USING brin (a int4_minmax_multi_ops);
+DROP TABLE brin_large_range;
+-- 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 a7a5b22d4f..76050af797 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4990,12 +4990,13 @@ List of access methods
 (0 rows)
 
 \dAc brin pg*.oid*
-                   List of operator classes
-  AM  | Input type | Storage type | Operator class | Default? 
-------+------------+--------------+----------------+----------
- brin | oid        |              | oid_bloom_ops  | no
- brin | oid        |              | oid_minmax_ops | yes
-(2 rows)
+                      List of operator classes
+  AM  | Input type | Storage type |    Operator class    | Default? 
+------+------------+--------------+----------------------+----------
+ brin | oid        |              | oid_bloom_ops        | no
+ brin | oid        |              | oid_minmax_multi_ops | no
+ brin | oid        |              | oid_minmax_ops       | yes
+(3 rows)
 
 \dAf spgist
           List of operator families
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 48ce3f7411..5480f979c6 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -67,14 +67,15 @@ WHERE p1.typtype not in ('p') AND p1.typname NOT LIKE E'\\_%'
      WHERE p2.typname = ('_' || p1.typname)::name AND
            p2.typelem = p1.oid and p1.typarray = p2.oid)
 ORDER BY p1.oid;
- oid  |        typname        
-------+-----------------------
+ oid  |           typname            
+------+------------------------------
   194 | pg_node_tree
  3361 | pg_ndistinct
  3402 | pg_dependencies
  4600 | pg_brin_bloom_summary
+ 4601 | pg_brin_minmax_multi_summary
  5017 | pg_mcv_list
-(5 rows)
+(6 rows)
 
 -- Make sure typarray points to a "true" array type of our own base
 SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 95927a7bae..c6e49affeb 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -80,7 +80,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 c02a981c78..1d7eb418a7 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -109,6 +109,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..f5ffcaea33
--- /dev/null
+++ b/src/test/regress/sql/brin_multi.sql
@@ -0,0 +1,403 @@
+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 = 7)
+);
+-- number of values must be <= 256
+CREATE INDEX brinidx_multi ON brintest_multi USING brin (
+	int8col int8_minmax_multi_ops(values_per_range = 257)
+);
+
+-- first create an index with a single page range, to force compaction
+-- due to exceeding the number of values per summary
+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
+);
+
+DROP INDEX brinidx_multi;
+
+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 building an index with many values, to force compaction of the buffer
+CREATE TABLE brin_large_range (a int4);
+INSERT INTO brin_large_range SELECT i FROM generate_series(1,10000) s(i);
+CREATE INDEX brin_large_range_idx ON brin_large_range USING brin (a int4_minmax_multi_ops);
+DROP TABLE brin_large_range;
+
+-- 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.30.2

  [text/x-patch] 0004-move-bloom-to-contrib-20210323.patch (82.2K, ../357fb6d7-649f-822e-ff43-16b39aed68ee@enterprisedb.com/5-0004-move-bloom-to-contrib-20210323.patch)
  download | inline diff:
From c1ccd88fc2dc8c671191c141b4d99550a8624fe2 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Mon, 22 Mar 2021 03:22:41 +0100
Subject: [PATCH 4/5] move bloom to contrib

---
 contrib/Makefile                              |   1 +
 contrib/brin_bloom/.gitignore                 |   4 +
 contrib/brin_bloom/Makefile                   |  23 +
 contrib/brin_bloom/brin_bloom--1.0.sql        | 358 ++++++++++++++
 .../brin => contrib/brin_bloom}/brin_bloom.c  |  19 +-
 contrib/brin_bloom/brin_bloom.control         |   6 +
 .../brin_bloom}/expected/brin_bloom.out       |  21 +
 .../brin_bloom}/sql/brin_bloom.sql            |  25 +
 src/backend/access/brin/Makefile              |   1 -
 src/include/catalog/pg_amop.dat               | 115 -----
 src/include/catalog/pg_amproc.dat             | 447 ------------------
 src/include/catalog/pg_opclass.dat            |  72 ---
 src/include/catalog/pg_opfamily.dat           |  38 --
 src/include/catalog/pg_proc.dat               |  34 --
 src/include/catalog/pg_type.dat               |   6 -
 src/test/regress/expected/opr_sanity.out      |   3 +-
 src/test/regress/expected/psql.out            |   3 +-
 src/test/regress/expected/type_sanity.out     |   3 +-
 src/test/regress/parallel_schedule            |   2 +-
 src/test/regress/serial_schedule              |   1 -
 20 files changed, 460 insertions(+), 722 deletions(-)
 create mode 100644 contrib/brin_bloom/.gitignore
 create mode 100644 contrib/brin_bloom/Makefile
 create mode 100644 contrib/brin_bloom/brin_bloom--1.0.sql
 rename {src/backend/access/brin => contrib/brin_bloom}/brin_bloom.c (97%)
 create mode 100644 contrib/brin_bloom/brin_bloom.control
 rename {src/test/regress => contrib/brin_bloom}/expected/brin_bloom.out (96%)
 rename {src/test/regress => contrib/brin_bloom}/sql/brin_bloom.sql (96%)

diff --git a/contrib/Makefile b/contrib/Makefile
index f27e458482..f878287429 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -12,6 +12,7 @@ SUBDIRS = \
 		bloom		\
 		btree_gin	\
 		btree_gist	\
+		brin_bloom	\
 		citext		\
 		cube		\
 		dblink		\
diff --git a/contrib/brin_bloom/.gitignore b/contrib/brin_bloom/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/contrib/brin_bloom/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/contrib/brin_bloom/Makefile b/contrib/brin_bloom/Makefile
new file mode 100644
index 0000000000..d8ca383cf1
--- /dev/null
+++ b/contrib/brin_bloom/Makefile
@@ -0,0 +1,23 @@
+# contrib/brin_bloom/Makefile
+
+MODULE_big = brin_bloom
+OBJS = \
+	$(WIN32RES) \
+	brin_bloom.o
+
+EXTENSION = brin_bloom
+DATA = brin_bloom--1.0.sql
+PGFILEDESC = "brin_bloom - BRIN bloom operator classes"
+
+REGRESS = brin_bloom
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/brin_bloom
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/brin_bloom/brin_bloom--1.0.sql b/contrib/brin_bloom/brin_bloom--1.0.sql
new file mode 100644
index 0000000000..bc3e9ca35f
--- /dev/null
+++ b/contrib/brin_bloom/brin_bloom--1.0.sql
@@ -0,0 +1,358 @@
+/* contrib/brin_bloom/brin_bloom--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION brin_bloom" to load this file. \quit
+
+CREATE TYPE brin_bloom_summary;
+
+-- BRIN bloom summary data type
+CREATE FUNCTION brin_bloom_summary_in(cstring)
+RETURNS brin_bloom_summary
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION brin_bloom_summary_out(brin_bloom_summary)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION brin_bloom_summary_recv(internal)
+RETURNS brin_bloom_summary
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION brin_bloom_summary_send(brin_bloom_summary)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE brin_bloom_summary (
+	INTERNALLENGTH = -1,
+	INPUT = brin_bloom_summary_in,
+	OUTPUT = brin_bloom_summary_out,
+	RECEIVE = brin_bloom_summary_recv,
+	SEND = brin_bloom_summary_send,
+	STORAGE = extended
+);
+
+-- BRIN support procedures
+CREATE FUNCTION brin_bloom_opcinfo(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION brin_bloom_add_value(internal, internal, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION brin_bloom_consistent(internal, internal, internal, int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION brin_bloom_union(internal, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION brin_bloom_options(internal)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE OPERATOR FAMILY datetime_bloom_ops USING brin;
+
+CREATE OPERATOR CLASS bpchar_bloom_ops
+FOR TYPE bpchar USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashbpchar(bpchar),
+STORAGE         bpchar;
+
+CREATE OPERATOR CLASS bytea_bloom_ops
+FOR TYPE bytea USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashvarlena(internal),
+STORAGE         bytea;
+
+CREATE OPERATOR CLASS char_bloom_ops
+FOR TYPE "char" USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashchar("char"),
+STORAGE         "char";
+
+CREATE OPERATOR CLASS date_bloom_ops
+FOR TYPE date USING brin FAMILY datetime_bloom_ops
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashint4(int4),
+STORAGE         date;
+
+CREATE OPERATOR FAMILY float_bloom_ops USING brin;
+
+CREATE OPERATOR CLASS float4_bloom_ops
+FOR TYPE float4 USING brin FAMILY float_bloom_ops
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashfloat4(float4),
+STORAGE         float4;
+
+CREATE OPERATOR CLASS float8_bloom_ops
+FOR TYPE float8 USING brin FAMILY float_bloom_ops
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashfloat8(float8),
+STORAGE         float8;
+
+CREATE OPERATOR FAMILY network_bloom_ops USING brin;
+
+CREATE OPERATOR CLASS inet_bloom_ops
+FOR TYPE inet USING brin FAMILY network_bloom_ops
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashinet(inet),
+STORAGE         inet;
+
+CREATE OPERATOR FAMILY integer_bloom_ops USING brin;
+
+CREATE OPERATOR CLASS int2_bloom_ops
+FOR TYPE int2 USING brin FAMILY integer_bloom_ops
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashint2(int2),
+STORAGE         int2;
+
+CREATE OPERATOR CLASS int4_bloom_ops
+FOR TYPE int4 USING brin FAMILY integer_bloom_ops
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashint4(int4),
+STORAGE         int4;
+
+CREATE OPERATOR CLASS int8_bloom_ops
+FOR TYPE int8 USING brin FAMILY integer_bloom_ops
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashint8(int8),
+STORAGE         int8;
+
+CREATE OPERATOR CLASS interval_bloom_ops
+FOR TYPE interval USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      interval_hash(interval),
+STORAGE         interval;
+
+CREATE OPERATOR CLASS macaddr_bloom_ops
+FOR TYPE macaddr USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashmacaddr(macaddr),
+STORAGE         macaddr;
+
+CREATE OPERATOR CLASS macaddr8_bloom_ops
+FOR TYPE macaddr8 USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashmacaddr8(macaddr8),
+STORAGE         macaddr8;
+
+CREATE OPERATOR CLASS name_bloom_ops
+FOR TYPE name USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashname(name),
+STORAGE         name;
+
+CREATE OPERATOR CLASS numeric_bloom_ops
+FOR TYPE numeric USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hash_numeric(numeric),
+STORAGE         numeric;
+
+CREATE OPERATOR CLASS oid_bloom_ops
+FOR TYPE oid USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashoid(oid),
+STORAGE         oid;
+
+CREATE OPERATOR CLASS pg_lsn_bloom_ops
+FOR TYPE pg_lsn USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      pg_lsn_hash(pg_lsn),
+STORAGE         pg_lsn;
+
+CREATE OPERATOR CLASS text_bloom_ops
+FOR TYPE text USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashtext(text),
+STORAGE         text;
+
+CREATE OPERATOR CLASS tid_bloom_ops
+FOR TYPE tid USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      hashtid(tid),
+STORAGE         tid;
+
+CREATE OPERATOR CLASS time_bloom_ops
+FOR TYPE time USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      time_hash(time),
+STORAGE         time;
+
+CREATE OPERATOR CLASS timestamp_bloom_ops
+FOR TYPE timestamp USING brin FAMILY datetime_bloom_ops
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      timestamp_hash(timestamp),
+STORAGE         timestamp;
+
+CREATE OPERATOR CLASS timestamptz_bloom_ops
+FOR TYPE timestamptz USING brin FAMILY datetime_bloom_ops
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      timestamp_hash(timestamp),
+STORAGE         timestamptz;
+
+CREATE OPERATOR CLASS timetz_bloom_ops
+FOR TYPE timetz USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      timetz_hash(timetz),
+STORAGE         timetz;
+
+CREATE OPERATOR CLASS uuid_bloom_ops
+FOR TYPE uuid USING brin
+AS
+    OPERATOR        1       =,
+    FUNCTION        1       brin_bloom_opcinfo(internal),
+    FUNCTION        2       brin_bloom_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_bloom_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_bloom_union(internal, internal, internal),
+    FUNCTION        5       brin_bloom_options(internal),
+    FUNCTION        11      uuid_hash(uuid),
+STORAGE         uuid;
diff --git a/src/backend/access/brin/brin_bloom.c b/contrib/brin_bloom/brin_bloom.c
similarity index 97%
rename from src/backend/access/brin/brin_bloom.c
rename to contrib/brin_bloom/brin_bloom.c
index 2214fb4d0c..51e96ed1ef 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/contrib/brin_bloom/brin_bloom.c
@@ -123,6 +123,7 @@
 #include "access/htup_details.h"
 #include "access/reloptions.h"
 #include "access/stratnum.h"
+#include "catalog/namespace.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_amop.h"
 #include "utils/builtins.h"
@@ -133,6 +134,9 @@
 
 #include <math.h>
 
+PG_MODULE_MAGIC;
+
+
 #define BloomEqualStrategyNumber	1
 
 /*
@@ -225,6 +229,18 @@ typedef struct BloomOptions
 #define BLOOM_SEED_1	0x71d924af
 #define BLOOM_SEED_2	0xba48b314
 
+
+PG_FUNCTION_INFO_V1(brin_bloom_summary_in);
+PG_FUNCTION_INFO_V1(brin_bloom_summary_out);
+PG_FUNCTION_INFO_V1(brin_bloom_summary_send);
+PG_FUNCTION_INFO_V1(brin_bloom_summary_recv);
+
+PG_FUNCTION_INFO_V1(brin_bloom_opcinfo);
+PG_FUNCTION_INFO_V1(brin_bloom_options);
+PG_FUNCTION_INFO_V1(brin_bloom_add_value);
+PG_FUNCTION_INFO_V1(brin_bloom_consistent);
+PG_FUNCTION_INFO_V1(brin_bloom_union);
+
 /*
  * Bloom Filter
  *
@@ -424,6 +440,7 @@ Datum
 brin_bloom_opcinfo(PG_FUNCTION_ARGS)
 {
 	BrinOpcInfo *result;
+	Oid	typoid = TypenameGetTypidExtended("brin_bloom_summary", false);
 
 	/*
 	 * opaque->strategy_procinfos is initialized lazily; here it is set to
@@ -438,7 +455,7 @@ brin_bloom_opcinfo(PG_FUNCTION_ARGS)
 	result->oi_regular_nulls = true;
 	result->oi_opaque = (BloomOpaque *)
 		MAXALIGN((char *) result + SizeofBrinOpcInfo(1));
-	result->oi_typcache[0] = lookup_type_cache(PG_BRIN_BLOOM_SUMMARYOID, 0);
+	result->oi_typcache[0] = lookup_type_cache(typoid, 0);
 
 	PG_RETURN_POINTER(result);
 }
diff --git a/contrib/brin_bloom/brin_bloom.control b/contrib/brin_bloom/brin_bloom.control
new file mode 100644
index 0000000000..b77048a4ac
--- /dev/null
+++ b/contrib/brin_bloom/brin_bloom.control
@@ -0,0 +1,6 @@
+# brin_bloom extension
+comment = 'support for BRIN bloom indexes'
+default_version = '1.0'
+module_pathname = '$libdir/brin_bloom'
+relocatable = true
+trusted = true
diff --git a/src/test/regress/expected/brin_bloom.out b/contrib/brin_bloom/expected/brin_bloom.out
similarity index 96%
rename from src/test/regress/expected/brin_bloom.out
rename to contrib/brin_bloom/expected/brin_bloom.out
index 32c56a996a..4544d09691 100644
--- a/src/test/regress/expected/brin_bloom.out
+++ b/contrib/brin_bloom/expected/brin_bloom.out
@@ -1,3 +1,24 @@
+CREATE EXTENSION brin_bloom;
+CREATE TABLE tenk1 (
+	unique1		int4,
+	unique2		int4,
+	two			int4,
+	four		int4,
+	ten			int4,
+	twenty		int4,
+	hundred		int4,
+	thousand	int4,
+	twothousand	int4,
+	fivethous	int4,
+	tenthous	int4,
+	odd			int4,
+	even		int4,
+	stringu1	name,
+	stringu2	name,
+	string4		name
+);
+\copy tenk1 from '../../src/test/regress/data/tenk.data'
+CREATE INDEX tenk1_unique1 ON tenk1 USING btree(unique1 int4_ops);
 CREATE TABLE brintest_bloom (byteacol bytea,
 	charcol "char",
 	namecol name,
diff --git a/src/test/regress/sql/brin_bloom.sql b/contrib/brin_bloom/sql/brin_bloom.sql
similarity index 96%
rename from src/test/regress/sql/brin_bloom.sql
rename to contrib/brin_bloom/sql/brin_bloom.sql
index 5d499208e3..fa79053ac5 100644
--- a/src/test/regress/sql/brin_bloom.sql
+++ b/contrib/brin_bloom/sql/brin_bloom.sql
@@ -1,3 +1,28 @@
+CREATE EXTENSION brin_bloom;
+
+CREATE TABLE tenk1 (
+	unique1		int4,
+	unique2		int4,
+	two			int4,
+	four		int4,
+	ten			int4,
+	twenty		int4,
+	hundred		int4,
+	thousand	int4,
+	twothousand	int4,
+	fivethous	int4,
+	tenthous	int4,
+	odd			int4,
+	even		int4,
+	stringu1	name,
+	stringu2	name,
+	string4		name
+);
+
+\copy tenk1 from '../../src/test/regress/data/tenk.data'
+
+CREATE INDEX tenk1_unique1 ON tenk1 USING btree(unique1 int4_ops);
+
 CREATE TABLE brintest_bloom (byteacol bytea,
 	charcol "char",
 	namecol name,
diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile
index a386cb71f1..75eb87ec10 100644
--- a/src/backend/access/brin/Makefile
+++ b/src/backend/access/brin/Makefile
@@ -14,7 +14,6 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = \
 	brin.o \
-	brin_bloom.o \
 	brin_inclusion.o \
 	brin_minmax.o \
 	brin_minmax_multi.o \
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 8135854163..51bef55a57 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1814,11 +1814,6 @@
   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)',
@@ -1836,11 +1831,6 @@
   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)',
@@ -1858,11 +1848,6 @@
   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',
@@ -2155,20 +2140,6 @@
   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 => 'int2',
-  amoprighttype => 'int2', amopstrategy => '1', amopopr => '=(int2,int2)',
-  amopmethod => 'brin' },
-
-{ amopfamily => 'brin/integer_bloom_ops', amoplefttype => 'int4',
-  amoprighttype => 'int4', amopstrategy => '1', amopopr => '=(int4,int4)',
-  amopmethod => 'brin' },
-
 # minmax text
 { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text',
   amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)',
@@ -2186,11 +2157,6 @@
   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)',
@@ -2225,11 +2191,6 @@
   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)',
@@ -2247,10 +2208,6 @@
   amoprighttype => 'tid', amopstrategy => '5', amopopr => '>(tid,tid)',
   amopmethod => 'brin' },
 
-# tid oid
-{ amopfamily => 'brin/tid_bloom_ops', amoplefttype => 'tid',
-  amoprighttype => 'tid', amopstrategy => '1', amopopr => '=(tid,tid)',
-  amopmethod => 'brin' },
 # minmax multi tid
 { amopfamily => 'brin/tid_minmax_multi_ops', amoplefttype => 'tid',
   amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)',
@@ -2400,14 +2357,6 @@
   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 => 'float8',
-  amoprighttype => 'float8', amopstrategy => '1', amopopr => '=(float8,float8)',
-  amopmethod => 'brin' },
-
 # minmax macaddr
 { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr',
   amoprighttype => 'macaddr', amopstrategy => '1',
@@ -2442,11 +2391,6 @@
   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',
@@ -2481,11 +2425,6 @@
   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)',
@@ -2520,11 +2459,6 @@
   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)',
@@ -2562,11 +2496,6 @@
   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)',
@@ -2601,11 +2530,6 @@
   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',
@@ -2898,20 +2822,6 @@
   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 => 'date',
-  amoprighttype => 'date', amopstrategy => '1', amopopr => '=(date,date)',
-  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',
@@ -2946,11 +2856,6 @@
   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)',
@@ -2985,11 +2890,6 @@
   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)',
@@ -3058,11 +2958,6 @@
   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)',
@@ -3097,11 +2992,6 @@
   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',
@@ -3180,11 +3070,6 @@
   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 f2c0ccfa1f..68c3342eeb 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -805,24 +805,6 @@
 { 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',
@@ -836,24 +818,6 @@
 { 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',
@@ -867,24 +831,6 @@
 { 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',
@@ -1132,58 +1078,6 @@
 { 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',
-  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',
@@ -1197,24 +1091,6 @@
 { 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' },
@@ -1244,23 +1120,6 @@
 { 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' },
-{ 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' },
@@ -1273,23 +1132,6 @@
 { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid',
   amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_minmax_union' },
 
-# bloom tid
-{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
-  amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_bloom_opcinfo' },
-{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
-  amprocrighttype => 'tid', amprocnum => '2',
-  amproc => 'brin_bloom_add_value' },
-{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
-  amprocrighttype => 'tid', amprocnum => '3',
-  amproc => 'brin_bloom_consistent' },
-{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
-  amprocrighttype => 'tid', amprocnum => '4', amproc => 'brin_bloom_union' },
-{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
-  amprocrighttype => 'tid', amprocnum => '5',
-  amproc => 'brin_bloom_options' },
-{ amprocfamily => 'brin/tid_bloom_ops', amproclefttype => 'tid',
-  amprocrighttype => 'tid', amprocnum => '11', amproc => 'hashtid' },
-
 # minmax multi tid
 { amprocfamily => 'brin/tid_minmax_multi_ops', amproclefttype => 'tid',
   amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_multi_opcinfo' },
@@ -1431,45 +1273,6 @@
   amprocrighttype => 'float4', amprocnum => '11',
   amproc => 'brin_minmax_multi_distance_float8' },
 
-# 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',
@@ -1504,26 +1307,6 @@
   amprocrighttype => 'macaddr', amprocnum => '11',
   amproc => 'brin_minmax_multi_distance_macaddr' },
 
-# 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',
@@ -1558,26 +1341,6 @@
   amprocrighttype => 'macaddr8', amprocnum => '11',
   amproc => 'brin_minmax_multi_distance_macaddr8' },
 
-# 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',
@@ -1611,24 +1374,6 @@
   amprocrighttype => 'inet', amprocnum => '11',
   amproc => 'brin_minmax_multi_distance_inet' },
 
-# 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',
@@ -1663,26 +1408,6 @@
   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 => 'hashbpchar' },
-
 # minmax time without time zone
 { amprocfamily => 'brin/time_minmax_ops', amproclefttype => 'time',
   amprocrighttype => 'time', amprocnum => '1',
@@ -1715,24 +1440,6 @@
   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',
-  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',
@@ -2004,62 +1711,6 @@
   amprocrighttype => 'timestamptz', amprocnum => '11',
   amproc => 'brin_minmax_multi_distance_date' },
 
-# 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',
@@ -2094,26 +1745,6 @@
   amprocrighttype => 'interval', amprocnum => '11',
   amproc => 'brin_minmax_multi_distance_interval' },
 
-# 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',
@@ -2148,26 +1779,6 @@
   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',
-  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' },
@@ -2228,26 +1839,6 @@
   amprocrighttype => 'numeric', amprocnum => '11',
   amproc => 'brin_minmax_multi_distance_numeric' },
 
-# 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',
@@ -2282,24 +1873,6 @@
   amprocrighttype => 'uuid', amprocnum => '11',
   amproc => 'brin_minmax_multi_distance_uuid' },
 
-# 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',
@@ -2357,26 +1930,6 @@
   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',
-  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 da25befefe..e510e28655 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -266,67 +266,40 @@
 { 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_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' },
 { 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_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_bloom_ops',
-  opcfamily => 'brin/tid_bloom_ops', opcintype => 'tid', opckeytype => 'tid',
-  opcdefault => 'f'},
 { opcmethod => 'brin', opcname => 'tid_minmax_multi_ops',
   opcfamily => 'brin/tid_minmax_multi_ops', opcintype => 'tid',
   opckeytype => 'tid', opcdefault => 'f' },
@@ -336,108 +309,72 @@
 { 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' },
 { 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_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' },
 { opcmethod => 'brin', opcname => 'bit_minmax_ops',
   opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' },
 { opcmethod => 'brin', opcname => 'varbit_minmax_ops',
@@ -449,9 +386,6 @@
 { 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' },
 
 # no brin opclass for record, anyarray
 
@@ -461,9 +395,6 @@
 { 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' },
 { opcmethod => 'brin', opcname => 'range_inclusion_ops',
   opcfamily => 'brin/range_inclusion_ops', opcintype => 'anyrange',
   opckeytype => 'anyrange' },
@@ -473,9 +404,6 @@
 { 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' },
 
 # 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 04edca6cb0..6218eade08 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -184,96 +184,62 @@
   opfmethod => 'brin', opfname => 'integer_minmax_ops' },
 { oid => '4602',
   opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' },
-{ oid => '4572',
-  opfmethod => 'brin', opfname => 'integer_bloom_ops' },
 { oid => '4055',
   opfmethod => 'brin', opfname => 'numeric_minmax_ops' },
 { oid => '4603',
   opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' },
 { oid => '4056',
   opfmethod => 'brin', opfname => 'text_minmax_ops' },
-{ oid => '4573',
-  opfmethod => 'brin', opfname => 'text_bloom_ops' },
-{ oid => '4574',
-  opfmethod => 'brin', opfname => 'numeric_bloom_ops' },
 { oid => '4058',
   opfmethod => 'brin', opfname => 'timetz_minmax_ops' },
 { oid => '4604',
   opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' },
-{ oid => '4575',
-  opfmethod => 'brin', opfname => 'timetz_bloom_ops' },
 { oid => '4059',
   opfmethod => 'brin', opfname => 'datetime_minmax_ops' },
 { oid => '4605',
   opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' },
-{ oid => '4576',
-  opfmethod => 'brin', opfname => 'datetime_bloom_ops' },
 { oid => '4062',
   opfmethod => 'brin', opfname => 'char_minmax_ops' },
-{ oid => '4577',
-  opfmethod => 'brin', opfname => 'char_bloom_ops' },
 { oid => '4064',
   opfmethod => 'brin', opfname => 'bytea_minmax_ops' },
-{ oid => '4578',
-  opfmethod => 'brin', opfname => 'bytea_bloom_ops' },
 { oid => '4065',
   opfmethod => 'brin', opfname => 'name_minmax_ops' },
-{ oid => '4579',
-  opfmethod => 'brin', opfname => 'name_bloom_ops' },
 { oid => '4068',
   opfmethod => 'brin', opfname => 'oid_minmax_ops' },
 { oid => '4606',
   opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' },
-{ oid => '4580',
-  opfmethod => 'brin', opfname => 'oid_bloom_ops' },
 { oid => '4069',
   opfmethod => 'brin', opfname => 'tid_minmax_ops' },
-{ oid => '4581',
-  opfmethod => 'brin', opfname => 'tid_bloom_ops' },
 { oid => '4607',
   opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' },
 { oid => '4070',
   opfmethod => 'brin', opfname => 'float_minmax_ops' },
 { oid => '4608',
   opfmethod => 'brin', opfname => 'float_minmax_multi_ops' },
-{ oid => '4582',
-  opfmethod => 'brin', opfname => 'float_bloom_ops' },
 { oid => '4074',
   opfmethod => 'brin', opfname => 'macaddr_minmax_ops' },
 { oid => '4609',
   opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' },
-{ oid => '4583',
-  opfmethod => 'brin', opfname => 'macaddr_bloom_ops' },
 { oid => '4109',
   opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' },
 { oid => '4610',
   opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' },
-{ oid => '4584',
-  opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' },
 { oid => '4075',
   opfmethod => 'brin', opfname => 'network_minmax_ops' },
 { oid => '4611',
   opfmethod => 'brin', opfname => 'network_minmax_multi_ops' },
 { oid => '4102',
   opfmethod => 'brin', opfname => 'network_inclusion_ops' },
-{ oid => '4585',
-  opfmethod => 'brin', opfname => 'network_bloom_ops' },
 { oid => '4076',
   opfmethod => 'brin', opfname => 'bpchar_minmax_ops' },
-{ oid => '4586',
-  opfmethod => 'brin', opfname => 'bpchar_bloom_ops' },
 { oid => '4077',
   opfmethod => 'brin', opfname => 'time_minmax_ops' },
 { oid => '4612',
   opfmethod => 'brin', opfname => 'time_minmax_multi_ops' },
-{ oid => '4587',
-  opfmethod => 'brin', opfname => 'time_bloom_ops' },
 { oid => '4078',
   opfmethod => 'brin', opfname => 'interval_minmax_ops' },
 { oid => '4613',
   opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' },
-{ oid => '4588',
-  opfmethod => 'brin', opfname => 'interval_bloom_ops' },
 { oid => '4079',
   opfmethod => 'brin', opfname => 'bit_minmax_ops' },
 { oid => '4080',
@@ -282,16 +248,12 @@
   opfmethod => 'brin', opfname => 'uuid_minmax_ops' },
 { oid => '4614',
   opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' },
-{ oid => '4589',
-  opfmethod => 'brin', opfname => 'uuid_bloom_ops' },
 { oid => '4103',
   opfmethod => 'brin', opfname => 'range_inclusion_ops' },
 { oid => '4082',
   opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' },
 { oid => '4615',
   opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' },
-{ oid => '4590',
-  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 183358c1ba..cb7d421d5a 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8309,26 +8309,6 @@
   proargtypes => 'internal internal internal',
   prosrc => 'brin_inclusion_union' },
 
-# BRIN bloom
-{ oid => '4591', descr => 'BRIN bloom support',
-  proname => 'brin_bloom_opcinfo', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'brin_bloom_opcinfo' },
-{ oid => '4592', descr => 'BRIN bloom support',
-  proname => 'brin_bloom_add_value', prorettype => 'bool',
-  proargtypes => 'internal internal internal internal',
-  prosrc => 'brin_bloom_add_value' },
-{ oid => '4593', descr => 'BRIN bloom support',
-  proname => 'brin_bloom_consistent', prorettype => 'bool',
-  proargtypes => 'internal internal internal int4',
-  prosrc => 'brin_bloom_consistent' },
-{ oid => '4594', descr => 'BRIN bloom support',
-  proname => 'brin_bloom_union', prorettype => 'bool',
-  proargtypes => 'internal internal internal',
-  prosrc => 'brin_bloom_union' },
-{ oid => '4595', 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',
@@ -11502,20 +11482,6 @@
   proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'unicode_is_normalized' },
 
-{ oid => '4596', descr => 'I/O',
-  proname => 'brin_bloom_summary_in', prorettype => 'pg_brin_bloom_summary',
-  proargtypes => 'cstring', prosrc => 'brin_bloom_summary_in' },
-{ oid => '4597', descr => 'I/O',
-  proname => 'brin_bloom_summary_out', prorettype => 'cstring',
-  proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_out' },
-{ oid => '4598', descr => 'I/O',
-  proname => 'brin_bloom_summary_recv', provolatile => 's',
-  prorettype => 'pg_brin_bloom_summary', proargtypes => 'internal',
-  prosrc => 'brin_bloom_summary_recv' },
-{ oid => '4599', descr => 'I/O',
-  proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea',
-  proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' },
-
 { oid => '4638', descr => 'I/O',
   proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary',
   proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 8c145c00be..387d25d66a 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -679,12 +679,6 @@
   typtype => 'p', typcategory => 'P', typinput => 'anycompatiblemultirange_in',
   typoutput => 'anycompatiblemultirange_out', typreceive => '-', typsend => '-',
   typalign => 'd', typstorage => 'x' },
-{ oid => '4600',
-  descr => 'BRIN bloom summary',
-  typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', typcategory => 'S',
-  typinput => 'brin_bloom_summary_in', typoutput => 'brin_bloom_summary_out',
-  typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send',
-  typalign => 'i', typstorage => 'x', typcollation => 'default' },
 { oid => '4601',
   descr => 'BRIN minmax-multi summary',
   typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index ef4b4444b9..254ca06d3d 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2037,7 +2037,6 @@ ORDER BY 1, 2, 3;
        2742 |           16 | @@
        3580 |            1 | <
        3580 |            1 | <<
-       3580 |            1 | =
        3580 |            2 | &<
        3580 |            2 | <=
        3580 |            3 | &&
@@ -2101,7 +2100,7 @@ ORDER BY 1, 2, 3;
        4000 |           28 | ^@
        4000 |           29 | <^
        4000 |           30 | >^
-(124 rows)
+(123 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 76050af797..ee45e37b84 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4993,10 +4993,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_multi_ops | no
  brin | oid        |              | oid_minmax_ops       | yes
-(3 rows)
+(2 rows)
 
 \dAf spgist
           List of operator families
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 5480f979c6..26f4b6097b 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -72,10 +72,9 @@ ORDER BY p1.oid;
   194 | pg_node_tree
  3361 | pg_ndistinct
  3402 | pg_dependencies
- 4600 | pg_brin_bloom_summary
  4601 | pg_brin_minmax_multi_summary
  5017 | pg_mcv_list
-(6 rows)
+(5 rows)
 
 -- Make sure typarray points to a "true" array type of our own base
 SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index c6e49affeb..1c6bbfcf54 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -80,7 +80,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview
 # ----------
 # Additional BRIN tests
 # ----------
-test: brin_bloom brin_multi
+test: brin_multi
 
 # ----------
 # Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 1d7eb418a7..dd3f3a9f0b 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -108,7 +108,6 @@ test: delete
 test: namespace
 test: prepared_xacts
 test: brin
-test: brin_bloom
 test: brin_multi
 test: gin
 test: gist
-- 
2.30.2

  [text/x-patch] 0005-move-minmax-multi-to-contrib-20210323.patch (124.7K, ../357fb6d7-649f-822e-ff43-16b39aed68ee@enterprisedb.com/6-0005-move-minmax-multi-to-contrib-20210323.patch)
  download | inline diff:
From 32bdd062ed87ca43102aaf360c126874af14a036 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Mon, 22 Mar 2021 05:10:37 +0100
Subject: [PATCH 5/5] move minmax-multi to contrib

---
 contrib/Makefile                              |   1 +
 contrib/brin_minmax_multi/.gitignore          |   4 +
 contrib/brin_minmax_multi/Makefile            |  23 +
 .../brin_minmax_multi--1.0.sql                | 552 ++++++++++++++++
 .../brin_minmax_multi}/brin_minmax_multi.c    |  63 +-
 .../brin_minmax_multi.control                 |   6 +
 .../expected/brin_multi.out                   |  21 +
 .../brin_minmax_multi}/sql/brin_multi.sql     |  25 +
 src/backend/access/brin/Makefile              |   1 -
 src/include/catalog/pg_amop.dat               | 545 ----------------
 src/include/catalog/pg_amproc.dat             | 597 ------------------
 src/include/catalog/pg_opclass.dat            |  57 --
 src/include/catalog/pg_opfamily.dat           |  28 -
 src/include/catalog/pg_proc.dat               |  85 +--
 src/include/catalog/pg_type.dat               |   6 -
 src/test/regress/expected/psql.out            |  11 +-
 src/test/regress/expected/type_sanity.out     |   7 +-
 src/test/regress/parallel_schedule            |   5 -
 src/test/regress/serial_schedule              |   1 -
 19 files changed, 703 insertions(+), 1335 deletions(-)
 create mode 100644 contrib/brin_minmax_multi/.gitignore
 create mode 100644 contrib/brin_minmax_multi/Makefile
 create mode 100644 contrib/brin_minmax_multi/brin_minmax_multi--1.0.sql
 rename {src/backend/access/brin => contrib/brin_minmax_multi}/brin_minmax_multi.c (96%)
 create mode 100644 contrib/brin_minmax_multi/brin_minmax_multi.control
 rename {src/test/regress => contrib/brin_minmax_multi}/expected/brin_multi.out (97%)
 rename {src/test/regress => contrib/brin_minmax_multi}/sql/brin_multi.sql (97%)

diff --git a/contrib/Makefile b/contrib/Makefile
index f878287429..bd131f8fc3 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -13,6 +13,7 @@ SUBDIRS = \
 		btree_gin	\
 		btree_gist	\
 		brin_bloom	\
+		brin_minmax_multi	\
 		citext		\
 		cube		\
 		dblink		\
diff --git a/contrib/brin_minmax_multi/.gitignore b/contrib/brin_minmax_multi/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/contrib/brin_minmax_multi/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/contrib/brin_minmax_multi/Makefile b/contrib/brin_minmax_multi/Makefile
new file mode 100644
index 0000000000..9ca5112339
--- /dev/null
+++ b/contrib/brin_minmax_multi/Makefile
@@ -0,0 +1,23 @@
+# contrib/brin_minmax_multi/Makefile
+
+MODULE_big = brin_minmax_multi
+OBJS = \
+	$(WIN32RES) \
+	brin_minmax_multi.o
+
+EXTENSION = brin_minmax_multi
+DATA = brin_minmax_multi--1.0.sql
+PGFILEDESC = "brin_minmax_multi - BRIN minmax-multi operator classes"
+
+REGRESS = brin_multi
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/brin_minmax_multi
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/brin_minmax_multi/brin_minmax_multi--1.0.sql b/contrib/brin_minmax_multi/brin_minmax_multi--1.0.sql
new file mode 100644
index 0000000000..0e32d38bd9
--- /dev/null
+++ b/contrib/brin_minmax_multi/brin_minmax_multi--1.0.sql
@@ -0,0 +1,552 @@
+/* contrib/brin_minmax_multi/brin_minmax_multi--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION brin_minmax_multi" to load this file. \quit
+
+CREATE TYPE brin_minmax_multi_summary;
+
+-- BRIN bloom summary data type
+CREATE FUNCTION brin_minmax_multi_summary_in(cstring)
+RETURNS brin_minmax_multi_summary
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION brin_minmax_multi_summary_out(brin_minmax_multi_summary)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION brin_minmax_multi_summary_recv(internal)
+RETURNS brin_minmax_multi_summary
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION brin_minmax_multi_summary_send(brin_minmax_multi_summary)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE brin_minmax_multi_summary (
+	INTERNALLENGTH = -1,
+	INPUT = brin_minmax_multi_summary_in,
+	OUTPUT = brin_minmax_multi_summary_out,
+	RECEIVE = brin_minmax_multi_summary_recv,
+	SEND = brin_minmax_multi_summary_send,
+	STORAGE = extended
+);
+
+-- BRIN support procedures
+CREATE FUNCTION brin_minmax_multi_opcinfo(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_add_value(internal, internal, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_consistent(internal, internal, internal, int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_union(internal, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_options(internal)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+-- distance functions
+
+CREATE FUNCTION brin_minmax_multi_distance_int2(int2, int2)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_int4(int4, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_int8(int8, int8)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_float4(float4, float4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_float8(float8, float8)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_numeric(numeric, numeric)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_tid(tid, tid)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_uuid(uuid, uuid)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_date(date, date)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_time(time, time)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_interval(interval, interval)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_timetz(timetz, timetz)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_pg_lsn(pg_lsn, pg_lsn)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_macaddr(macaddr, macaddr)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_macaddr8(macaddr8, macaddr8)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_inet(inet, inet)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE FUNCTION brin_minmax_multi_distance_timestamp(timestamp, timestamp)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE;
+
+CREATE OPERATOR FAMILY datetime_minmax_multi_ops USING brin;
+
+CREATE OPERATOR CLASS date_minmax_multi_ops
+FOR TYPE date USING brin FAMILY datetime_minmax_multi_ops
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_date(date,date),
+STORAGE         date;
+
+CREATE OPERATOR FAMILY float_minmax_multi_ops USING brin;
+
+CREATE OPERATOR CLASS float4_minmax_multi_ops
+FOR TYPE float4 USING brin FAMILY float_minmax_multi_ops
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_float4(float4,float4),
+STORAGE         float4;
+
+CREATE OPERATOR CLASS float8_minmax_multi_ops
+FOR TYPE float8 USING brin FAMILY float_minmax_multi_ops
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_float8(float8,float8),
+STORAGE         float8;
+
+ALTER OPERATOR FAMILY float_minmax_multi_ops USING brin ADD
+
+    OPERATOR        1       <(float4,float8),
+    OPERATOR        2       <=(float4,float8),
+    OPERATOR        3       =(float4,float8),
+    OPERATOR        4       >=(float4,float8),
+    OPERATOR        5       >(float4,float8),
+
+    OPERATOR        1       <(float8,float4),
+    OPERATOR        2       <=(float8,float4),
+    OPERATOR        3       =(float8,float4),
+    OPERATOR        4       >=(float8,float4),
+    OPERATOR        5       >(float8,float4);
+
+CREATE OPERATOR FAMILY network_minmax_multi_ops USING brin;
+
+CREATE OPERATOR CLASS inet_minmax_multi_ops
+FOR TYPE inet USING brin FAMILY network_minmax_multi_ops
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_inet(inet,inet),
+STORAGE         inet;
+
+CREATE OPERATOR FAMILY integer_minmax_multi_ops USING brin;
+
+CREATE OPERATOR CLASS int2_minmax_multi_ops
+FOR TYPE int2 USING brin FAMILY integer_minmax_multi_ops
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_int2(int2,int2),
+STORAGE         int2;
+
+CREATE OPERATOR CLASS int4_minmax_multi_ops
+FOR TYPE int4 USING brin FAMILY integer_minmax_multi_ops
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_int4(int4,int4),
+STORAGE         int4;
+
+CREATE OPERATOR CLASS int8_minmax_multi_ops
+FOR TYPE int8 USING brin FAMILY integer_minmax_multi_ops
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_int8(int8,int8),
+STORAGE         int8;
+
+ALTER OPERATOR FAMILY integer_minmax_multi_ops USING brin ADD
+
+    OPERATOR        1       <(int2,int4),
+    OPERATOR        2       <=(int2,int4),
+    OPERATOR        3       =(int2,int4),
+    OPERATOR        4       >=(int2,int4),
+    OPERATOR        5       >(int2,int4),
+
+    OPERATOR        1       <(int2,int8),
+    OPERATOR        2       <=(int2,int8),
+    OPERATOR        3       =(int2,int8),
+    OPERATOR        4       >=(int2,int8),
+    OPERATOR        5       >(int2,int8),
+
+    OPERATOR        1       <(int4,int2),
+    OPERATOR        2       <=(int4,int2),
+    OPERATOR        3       =(int4,int2),
+    OPERATOR        4       >=(int4,int2),
+    OPERATOR        5       >(int4,int2),
+
+    OPERATOR        1       <(int4,int8),
+    OPERATOR        2       <=(int4,int8),
+    OPERATOR        3       =(int4,int8),
+    OPERATOR        4       >=(int4,int8),
+    OPERATOR        5       >(int4,int8),
+
+    OPERATOR        1       <(int8,int2),
+    OPERATOR        2       <=(int8,int2),
+    OPERATOR        3       =(int8,int2),
+    OPERATOR        4       >=(int8,int2),
+    OPERATOR        5       >(int8,int2),
+
+    OPERATOR        1       <(int8,int4),
+    OPERATOR        2       <=(int8,int4),
+    OPERATOR        3       =(int8,int4),
+    OPERATOR        4       >=(int8,int4),
+    OPERATOR        5       >(int8,int4);
+
+CREATE OPERATOR CLASS interval_minmax_multi_ops
+FOR TYPE interval USING brin
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_interval(interval,interval),
+STORAGE         interval;
+
+CREATE OPERATOR CLASS macaddr_minmax_multi_ops
+FOR TYPE macaddr USING brin
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_macaddr(macaddr,macaddr),
+STORAGE         macaddr;
+
+CREATE OPERATOR CLASS macaddr8_minmax_multi_ops
+FOR TYPE macaddr8 USING brin
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_macaddr8(macaddr8,macaddr8),
+STORAGE         macaddr8;
+
+CREATE OPERATOR CLASS numeric_minmax_multi_ops
+FOR TYPE numeric USING brin
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_numeric(numeric,numeric),
+STORAGE         numeric;
+
+CREATE OPERATOR CLASS oid_minmax_multi_ops
+FOR TYPE oid USING brin
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_int4(int4,int4),
+STORAGE         oid;
+
+CREATE OPERATOR CLASS pg_lsn_minmax_multi_ops
+FOR TYPE pg_lsn USING brin
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_pg_lsn(pg_lsn,pg_lsn),
+STORAGE         pg_lsn;
+
+CREATE OPERATOR CLASS tid_minmax_multi_ops
+FOR TYPE tid USING brin
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_tid(tid,tid),
+STORAGE         tid;
+
+CREATE OPERATOR CLASS time_minmax_multi_ops
+FOR TYPE time USING brin
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_time(time,time),
+STORAGE         time;
+
+CREATE OPERATOR CLASS timestamp_minmax_multi_ops
+FOR TYPE timestamp USING brin FAMILY datetime_minmax_multi_ops
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_timestamp(timestamp,timestamp),
+STORAGE         timestamp;
+
+CREATE OPERATOR CLASS timestamptz_minmax_multi_ops
+FOR TYPE timestamptz USING brin FAMILY datetime_minmax_multi_ops
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_timestamp(timestamp,timestamp),
+STORAGE         timestamptz;
+
+ALTER OPERATOR FAMILY datetime_minmax_multi_ops USING brin ADD
+
+    OPERATOR        1       <(timestamp,timestamptz),
+    OPERATOR        2       <=(timestamp,timestamptz),
+    OPERATOR        3       =(timestamp,timestamptz),
+    OPERATOR        4       >=(timestamp,timestamptz),
+    OPERATOR        5       >(timestamp,timestamptz),
+
+    OPERATOR        1       <(timestamp,date),
+    OPERATOR        2       <=(timestamp,date),
+    OPERATOR        3       =(timestamp,date),
+    OPERATOR        4       >=(timestamp,date),
+    OPERATOR        5       >(timestamp,date),
+
+    OPERATOR        1       <(date,timestamp),
+    OPERATOR        2       <=(date,timestamp),
+    OPERATOR        3       =(date,timestamp),
+    OPERATOR        4       >=(date,timestamp),
+    OPERATOR        5       >(date,timestamp),
+
+    OPERATOR        1       <(date,timestamptz),
+    OPERATOR        2       <=(date,timestamptz),
+    OPERATOR        3       =(date,timestamptz),
+    OPERATOR        4       >=(date,timestamptz),
+    OPERATOR        5       >(date,timestamptz),
+
+    OPERATOR        1       <(timestamptz,timestamp),
+    OPERATOR        2       <=(timestamptz,timestamp),
+    OPERATOR        3       =(timestamptz,timestamp),
+    OPERATOR        4       >=(timestamptz,timestamp),
+    OPERATOR        5       >(timestamptz,timestamp),
+
+    OPERATOR        1       <(timestamptz,date),
+    OPERATOR        2       <=(timestamptz,date),
+    OPERATOR        3       =(timestamptz,date),
+    OPERATOR        4       >=(timestamptz,date),
+    OPERATOR        5       >(timestamptz,date);
+
+CREATE OPERATOR CLASS timetz_minmax_multi_ops
+FOR TYPE timetz USING brin
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_timetz(timetz,timetz),
+STORAGE         timetz;
+
+CREATE OPERATOR CLASS uuid_minmax_multi_ops
+FOR TYPE uuid USING brin
+AS
+    OPERATOR        1       <,
+    OPERATOR        2       <=,
+    OPERATOR        3       =,
+    OPERATOR        4       >=,
+    OPERATOR        5       >,
+    FUNCTION        1       brin_minmax_multi_opcinfo(internal),
+    FUNCTION        2       brin_minmax_multi_add_value(internal, internal, internal, internal),
+    FUNCTION        3       brin_minmax_multi_consistent(internal, internal, internal, int4),
+    FUNCTION        4       brin_minmax_multi_union(internal, internal, internal),
+    FUNCTION        5       brin_minmax_multi_options(internal),
+    FUNCTION       11       brin_minmax_multi_distance_uuid(uuid,uuid),
+STORAGE         uuid;
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/contrib/brin_minmax_multi/brin_minmax_multi.c
similarity index 96%
rename from src/backend/access/brin/brin_minmax_multi.c
rename to contrib/brin_minmax_multi/brin_minmax_multi.c
index 1ca86b7fb7..bb6a9b9157 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/contrib/brin_minmax_multi/brin_minmax_multi.c
@@ -66,6 +66,7 @@
 #include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "access/htup_details.h"
+#include "catalog/namespace.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_am.h"
 #include "catalog/pg_amop.h"
@@ -83,6 +84,8 @@
 #include "utils/timestamp.h"
 #include "utils/uuid.h"
 
+PG_MODULE_MAGIC;
+
 /*
  * Additional SQL level support functions
  *
@@ -257,6 +260,63 @@ static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc,
 													uint16 attno, Oid subtype,
 													uint16 strategynum);
 
+Datum brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_float4(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_float8(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_int2(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_int4(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_int8(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_date(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_time(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_timetz(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_timestamp(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_pg_lsn(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_macaddr(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_macaddr8(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_add_value(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_consistent(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_union(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_options(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_summary_in(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_summary_out(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_summary_recv(PG_FUNCTION_ARGS);
+Datum brin_minmax_multi_summary_send(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(brin_minmax_multi_summary_in);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_summary_out);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_summary_send);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_summary_recv);
+
+PG_FUNCTION_INFO_V1(brin_minmax_multi_opcinfo);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_options);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_add_value);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_consistent);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_union);
+
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_int2);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_int4);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_int8);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_float4);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_float8);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_numeric);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_tid);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_uuid);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_date);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_time);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_interval);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_timetz);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_pg_lsn);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_macaddr);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_macaddr8);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_inet);
+PG_FUNCTION_INFO_V1(brin_minmax_multi_distance_timestamp);
+
+
 typedef struct compare_context
 {
 	FmgrInfo   *cmpFn;
@@ -1782,6 +1842,7 @@ Datum
 brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 {
 	BrinOpcInfo *result;
+	Oid	typoid = TypenameGetTypidExtended("brin_minmax_multi_summary", false);
 
 	/*
 	 * opaque->strategy_procinfos is initialized lazily; here it is set to
@@ -1794,7 +1855,7 @@ brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
 	result->oi_regular_nulls = true;
 	result->oi_opaque = (MinmaxMultiOpaque *)
 		MAXALIGN((char *) result + SizeofBrinOpcInfo(1));
-	result->oi_typcache[0] = lookup_type_cache(PG_BRIN_MINMAX_MULTI_SUMMARYOID, 0);
+	result->oi_typcache[0] = lookup_type_cache(typoid, 0);
 
 	PG_RETURN_POINTER(result);
 }
diff --git a/contrib/brin_minmax_multi/brin_minmax_multi.control b/contrib/brin_minmax_multi/brin_minmax_multi.control
new file mode 100644
index 0000000000..71e614867a
--- /dev/null
+++ b/contrib/brin_minmax_multi/brin_minmax_multi.control
@@ -0,0 +1,6 @@
+# brin_minmax_multi extension
+comment = 'support for BRIN minmax-multi indexes'
+default_version = '1.0'
+module_pathname = '$libdir/brin_minmax_multi'
+relocatable = true
+trusted = true
diff --git a/src/test/regress/expected/brin_multi.out b/contrib/brin_minmax_multi/expected/brin_multi.out
similarity index 97%
rename from src/test/regress/expected/brin_multi.out
rename to contrib/brin_minmax_multi/expected/brin_multi.out
index 0a7e4b8060..027d45c308 100644
--- a/src/test/regress/expected/brin_multi.out
+++ b/contrib/brin_minmax_multi/expected/brin_multi.out
@@ -1,3 +1,24 @@
+CREATE EXTENSION brin_minmax_multi;
+CREATE TABLE tenk1 (
+	unique1		int4,
+	unique2		int4,
+	two			int4,
+	four		int4,
+	ten			int4,
+	twenty		int4,
+	hundred		int4,
+	thousand	int4,
+	twothousand	int4,
+	fivethous	int4,
+	tenthous	int4,
+	odd			int4,
+	even		int4,
+	stringu1	name,
+	stringu2	name,
+	string4		name
+);
+\copy tenk1 from '../../src/test/regress/data/tenk.data'
+CREATE INDEX tenk1_unique1 ON tenk1 USING btree(unique1 int4_ops);
 CREATE TABLE brintest_multi (
 	int8col bigint,
 	int2col smallint,
diff --git a/src/test/regress/sql/brin_multi.sql b/contrib/brin_minmax_multi/sql/brin_multi.sql
similarity index 97%
rename from src/test/regress/sql/brin_multi.sql
rename to contrib/brin_minmax_multi/sql/brin_multi.sql
index f5ffcaea33..c7b87c1ca3 100644
--- a/src/test/regress/sql/brin_multi.sql
+++ b/contrib/brin_minmax_multi/sql/brin_multi.sql
@@ -1,3 +1,28 @@
+CREATE EXTENSION brin_minmax_multi;
+
+CREATE TABLE tenk1 (
+	unique1		int4,
+	unique2		int4,
+	two			int4,
+	four		int4,
+	ten			int4,
+	twenty		int4,
+	hundred		int4,
+	thousand	int4,
+	twothousand	int4,
+	fivethous	int4,
+	tenthous	int4,
+	odd			int4,
+	even		int4,
+	stringu1	name,
+	stringu2	name,
+	string4		name
+);
+
+\copy tenk1 from '../../src/test/regress/data/tenk.data'
+
+CREATE INDEX tenk1_unique1 ON tenk1 USING btree(unique1 int4_ops);
+
 CREATE TABLE brintest_multi (
 	int8col bigint,
 	int2col smallint,
diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile
index 75eb87ec10..468e1e289a 100644
--- a/src/backend/access/brin/Makefile
+++ b/src/backend/access/brin/Makefile
@@ -16,7 +16,6 @@ OBJS = \
 	brin.o \
 	brin_inclusion.o \
 	brin_minmax.o \
-	brin_minmax_multi.o \
 	brin_pageops.o \
 	brin_revmap.o \
 	brin_tuple.o \
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 51bef55a57..0f7ff63669 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1994,152 +1994,6 @@
   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' },
-
 # minmax text
 { amopfamily => 'brin/text_minmax_ops', amoplefttype => 'text',
   amoprighttype => 'text', amopstrategy => '1', amopopr => '<(text,text)',
@@ -2174,23 +2028,6 @@
   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' },
-
 # minmax tid
 { amopfamily => 'brin/tid_minmax_ops', amoplefttype => 'tid',
   amoprighttype => 'tid', amopstrategy => '1', amopopr => '<(tid,tid)',
@@ -2208,23 +2045,6 @@
   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',
@@ -2291,72 +2111,6 @@
   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' },
-
 # minmax macaddr
 { amopfamily => 'brin/macaddr_minmax_ops', amoplefttype => 'macaddr',
   amoprighttype => 'macaddr', amopstrategy => '1',
@@ -2374,23 +2128,6 @@
   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' },
-
 # minmax macaddr8
 { amopfamily => 'brin/macaddr8_minmax_ops', amoplefttype => 'macaddr8',
   amoprighttype => 'macaddr8', amopstrategy => '1',
@@ -2408,23 +2145,6 @@
   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' },
-
 # minmax inet
 { amopfamily => 'brin/network_minmax_ops', amoplefttype => 'inet',
   amoprighttype => 'inet', amopstrategy => '1', amopopr => '<(inet,inet)',
@@ -2442,23 +2162,6 @@
   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' },
-
 # inclusion inet
 { amopfamily => 'brin/network_inclusion_ops', amoplefttype => 'inet',
   amoprighttype => 'inet', amopstrategy => '3', amopopr => '&&(inet,inet)',
@@ -2513,23 +2216,6 @@
   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' },
-
 # minmax datetime (date, timestamp, timestamptz)
 
 { amopfamily => 'brin/datetime_minmax_ops', amoplefttype => 'timestamp',
@@ -2676,152 +2362,6 @@
   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' },
-
 # minmax interval
 { amopfamily => 'brin/interval_minmax_ops', amoplefttype => 'interval',
   amoprighttype => 'interval', amopstrategy => '1',
@@ -2839,23 +2379,6 @@
   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' },
-
 # minmax time with time zone
 { amopfamily => 'brin/timetz_minmax_ops', amoplefttype => 'timetz',
   amoprighttype => 'timetz', amopstrategy => '1', amopopr => '<(timetz,timetz)',
@@ -2873,23 +2396,6 @@
   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' },
-
 # minmax bit
 { amopfamily => 'brin/bit_minmax_ops', amoplefttype => 'bit',
   amoprighttype => 'bit', amopstrategy => '1', amopopr => '<(bit,bit)',
@@ -2941,23 +2447,6 @@
   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' },
-
 # minmax uuid
 { amopfamily => 'brin/uuid_minmax_ops', amoplefttype => 'uuid',
   amoprighttype => 'uuid', amopstrategy => '1', amopopr => '<(uuid,uuid)',
@@ -2975,23 +2464,6 @@
   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' },
-
 # inclusion range types
 { amopfamily => 'brin/range_inclusion_ops', amoplefttype => 'anyrange',
   amoprighttype => 'anyrange', amopstrategy => '1',
@@ -3053,23 +2525,6 @@
   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' },
-
 # 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 68c3342eeb..9192a66a88 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -932,152 +932,6 @@
 { 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' },
-
 # minmax text
 { amprocfamily => 'brin/text_minmax_ops', amproclefttype => 'text',
   amprocrighttype => 'text', amprocnum => '1',
@@ -1103,23 +957,6 @@
 { 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' },
-
 # minmax tid
 { amprocfamily => 'brin/tid_minmax_ops', amproclefttype => 'tid',
   amprocrighttype => 'tid', amprocnum => '1', amproc => 'brin_minmax_opcinfo' },
@@ -1132,23 +969,6 @@
 { 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',
@@ -1199,80 +1019,6 @@
   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' },
-
 # minmax macaddr
 { amprocfamily => 'brin/macaddr_minmax_ops', amproclefttype => 'macaddr',
   amprocrighttype => 'macaddr', amprocnum => '1',
@@ -1287,26 +1033,6 @@
   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' },
-
 # minmax macaddr8
 { amprocfamily => 'brin/macaddr8_minmax_ops', amproclefttype => 'macaddr8',
   amprocrighttype => 'macaddr8', amprocnum => '1',
@@ -1321,26 +1047,6 @@
   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' },
-
 # minmax inet
 { amprocfamily => 'brin/network_minmax_ops', amproclefttype => 'inet',
   amprocrighttype => 'inet', amprocnum => '1',
@@ -1354,26 +1060,6 @@
 { 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' },
-
 # inclusion inet
 { amprocfamily => 'brin/network_inclusion_ops', amproclefttype => 'inet',
   amprocrighttype => 'inet', amprocnum => '1',
@@ -1421,25 +1107,6 @@
 { 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' },
-
 # minmax datetime (date, timestamp, timestamptz)
 { amprocfamily => 'brin/datetime_minmax_ops', amproclefttype => 'timestamp',
   amprocrighttype => 'timestamp', amprocnum => '1',
@@ -1547,170 +1214,6 @@
   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_time' },
-{ 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_time' },
-{ 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_time' },
-{ 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_time' },
-{ 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_timestamp' },
-{ 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_timestamp' },
-{ 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_date' },
-{ 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_date' },
-{ 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_date' },
-
 # minmax interval
 { amprocfamily => 'brin/interval_minmax_ops', amproclefttype => 'interval',
   amprocrighttype => 'interval', amprocnum => '1',
@@ -1725,26 +1228,6 @@
   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' },
-
 # minmax time with time zone
 { amprocfamily => 'brin/timetz_minmax_ops', amproclefttype => 'timetz',
   amprocrighttype => 'timetz', amprocnum => '1',
@@ -1759,26 +1242,6 @@
   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' },
-
 # minmax bit
 { amprocfamily => 'brin/bit_minmax_ops', amproclefttype => 'bit',
   amprocrighttype => 'bit', amprocnum => '1', amproc => 'brin_minmax_opcinfo' },
@@ -1819,26 +1282,6 @@
   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' },
-
 # minmax uuid
 { amprocfamily => 'brin/uuid_minmax_ops', amproclefttype => 'uuid',
   amprocrighttype => 'uuid', amprocnum => '1',
@@ -1853,26 +1296,6 @@
   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' },
-
 # inclusion range types
 { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
   amprocrighttype => 'anyrange', amprocnum => '1',
@@ -1910,26 +1333,6 @@
   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' },
-
 # 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 e510e28655..24b1433e1f 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -275,64 +275,34 @@
 { 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 => '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 => '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 => 'text_minmax_ops',
   opcfamily => 'brin/text_minmax_ops', opcintype => 'text',
   opckeytype => 'text' },
 { 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 => '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 => '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 => '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 => '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 => '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_inclusion_ops',
   opcfamily => 'brin/network_inclusion_ops', opcintype => 'inet',
   opckeytype => 'inet' },
@@ -342,39 +312,21 @@
 { 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 => '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 => '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 => '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 => '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 => '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 => 'bit_minmax_ops',
   opcfamily => 'brin/bit_minmax_ops', opcintype => 'bit', opckeytype => 'bit' },
 { opcmethod => 'brin', opcname => 'varbit_minmax_ops',
@@ -383,27 +335,18 @@
 { 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' },
 
 # 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_minmax_multi_ops',
-  opcfamily => 'brin/uuid_minmax_multi_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_minmax_multi_ops',
-  opcfamily => 'brin/pg_lsn_minmax_multi_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 6218eade08..57e5aa0d8b 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -182,22 +182,14 @@
   opfmethod => 'gin', opfname => 'jsonb_path_ops' },
 { oid => '4054',
   opfmethod => 'brin', opfname => 'integer_minmax_ops' },
-{ oid => '4602',
-  opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' },
 { oid => '4055',
   opfmethod => 'brin', opfname => 'numeric_minmax_ops' },
-{ oid => '4603',
-  opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' },
 { oid => '4056',
   opfmethod => 'brin', opfname => 'text_minmax_ops' },
 { oid => '4058',
   opfmethod => 'brin', opfname => 'timetz_minmax_ops' },
-{ oid => '4604',
-  opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' },
 { oid => '4059',
   opfmethod => 'brin', opfname => 'datetime_minmax_ops' },
-{ oid => '4605',
-  opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' },
 { oid => '4062',
   opfmethod => 'brin', opfname => 'char_minmax_ops' },
 { oid => '4064',
@@ -206,54 +198,34 @@
   opfmethod => 'brin', opfname => 'name_minmax_ops' },
 { oid => '4068',
   opfmethod => 'brin', opfname => 'oid_minmax_ops' },
-{ oid => '4606',
-  opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' },
 { oid => '4069',
   opfmethod => 'brin', opfname => 'tid_minmax_ops' },
-{ oid => '4607',
-  opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' },
 { oid => '4070',
   opfmethod => 'brin', opfname => 'float_minmax_ops' },
-{ oid => '4608',
-  opfmethod => 'brin', opfname => 'float_minmax_multi_ops' },
 { oid => '4074',
   opfmethod => 'brin', opfname => 'macaddr_minmax_ops' },
-{ oid => '4609',
-  opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' },
 { oid => '4109',
   opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' },
-{ oid => '4610',
-  opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' },
 { oid => '4075',
   opfmethod => 'brin', opfname => 'network_minmax_ops' },
-{ oid => '4611',
-  opfmethod => 'brin', opfname => 'network_minmax_multi_ops' },
 { oid => '4102',
   opfmethod => 'brin', opfname => 'network_inclusion_ops' },
 { oid => '4076',
   opfmethod => 'brin', opfname => 'bpchar_minmax_ops' },
 { oid => '4077',
   opfmethod => 'brin', opfname => 'time_minmax_ops' },
-{ oid => '4612',
-  opfmethod => 'brin', opfname => 'time_minmax_multi_ops' },
 { oid => '4078',
   opfmethod => 'brin', opfname => 'interval_minmax_ops' },
-{ oid => '4613',
-  opfmethod => 'brin', opfname => 'interval_minmax_multi_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 => '4614',
-  opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' },
 { oid => '4103',
   opfmethod => 'brin', opfname => 'range_inclusion_ops' },
 { oid => '4082',
   opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' },
-{ oid => '4615',
-  opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_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 cb7d421d5a..ca240cef1a 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8221,76 +8221,7 @@
   proname => 'brin_minmax_union', prorettype => 'bool',
   proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' },
 
-# BRIN minmax multi
-{ oid => '4616', descr => 'BRIN multi minmax support',
-  proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' },
-{ oid => '4617', 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 => '4618', descr => 'BRIN multi minmax support',
-  proname => 'brin_minmax_multi_consistent', prorettype => 'bool',
-  proargtypes => 'internal internal internal int4',
-  prosrc => 'brin_minmax_multi_consistent' },
-{ oid => '4619', descr => 'BRIN multi minmax support',
-  proname => 'brin_minmax_multi_union', prorettype => 'bool',
-  proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' },
-{ oid => '4620', descr => 'BRIN multi minmax support',
-  proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f',
-  proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' },
-
-{ oid => '4621', descr => 'BRIN multi minmax int2 distance',
-  proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' },
-{ oid => '4622', descr => 'BRIN multi minmax int4 distance',
-  proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' },
-{ oid => '4623', descr => 'BRIN multi minmax int8 distance',
-  proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' },
-{ oid => '4624', descr => 'BRIN multi minmax float4 distance',
-  proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' },
-{ oid => '4625', descr => 'BRIN multi minmax float8 distance',
-  proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' },
-{ oid => '4626', descr => 'BRIN multi minmax numeric distance',
-  proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' },
-{ oid => '4627', descr => 'BRIN multi minmax tid distance',
-  proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' },
-{ oid => '4628', descr => 'BRIN multi minmax uuid distance',
-  proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' },
-{ oid => '4629', descr => 'BRIN multi minmax date distance',
-  proname => 'brin_minmax_multi_distance_date', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_date' },
-{ oid => '4630', descr => 'BRIN multi minmax time distance',
-  proname => 'brin_minmax_multi_distance_time', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' },
-{ oid => '4631', descr => 'BRIN multi minmax interval distance',
-  proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' },
-{ oid => '4632', descr => 'BRIN multi minmax timetz distance',
-  proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' },
-{ oid => '4633', 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 => '4634', descr => 'BRIN multi minmax macaddr distance',
-  proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' },
-{ oid => '4635', descr => 'BRIN multi minmax macaddr8 distance',
-  proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' },
-{ oid => '4636', descr => 'BRIN multi minmax inet distance',
-  proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' },
-{ oid => '4637', descr => 'BRIN multi minmax timestamp distance',
-  proname => 'brin_minmax_multi_distance_timestamp', prorettype => 'float8',
-  proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timestamp' },
+
 
 # BRIN inclusion
 { oid => '4105', descr => 'BRIN inclusion support',
@@ -11482,18 +11413,4 @@
   proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'unicode_is_normalized' },
 
-{ oid => '4638', descr => 'I/O',
-  proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary',
-  proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' },
-{ oid => '4639', descr => 'I/O',
-  proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring',
-  proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' },
-{ oid => '4640', descr => 'I/O',
-  proname => 'brin_minmax_multi_summary_recv', provolatile => 's',
-  prorettype => 'pg_brin_minmax_multi_summary', proargtypes => 'internal',
-  prosrc => 'brin_minmax_multi_summary_recv' },
-{ oid => '4641', descr => 'I/O',
-  proname => 'brin_minmax_multi_summary_send', provolatile => 's', prorettype => 'bytea',
-  proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_send' },
-
 ]
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 387d25d66a..920dbbbfeb 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -679,10 +679,4 @@
   typtype => 'p', typcategory => 'P', typinput => 'anycompatiblemultirange_in',
   typoutput => 'anycompatiblemultirange_out', typreceive => '-', typsend => '-',
   typalign => 'd', typstorage => 'x' },
-{ oid => '4601',
-  descr => 'BRIN minmax-multi summary',
-  typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', typcategory => 'S',
-  typinput => 'brin_minmax_multi_summary_in', typoutput => 'brin_minmax_multi_summary_out',
-  typreceive => 'brin_minmax_multi_summary_recv', typsend => 'brin_minmax_multi_summary_send',
-  typalign => 'i', typstorage => 'x', typcollation => 'default' },
 ]
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index ee45e37b84..7204fdb0b4 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4990,12 +4990,11 @@ 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_minmax_multi_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_minmax_ops | yes
+(1 row)
 
 \dAf spgist
           List of operator families
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 26f4b6097b..0c74dc96a8 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -67,14 +67,13 @@ WHERE p1.typtype not in ('p') AND p1.typname NOT LIKE E'\\_%'
      WHERE p2.typname = ('_' || p1.typname)::name AND
            p2.typelem = p1.oid and p1.typarray = p2.oid)
 ORDER BY p1.oid;
- oid  |           typname            
-------+------------------------------
+ oid  |     typname     
+------+-----------------
   194 | pg_node_tree
  3361 | pg_ndistinct
  3402 | pg_dependencies
- 4601 | pg_brin_minmax_multi_summary
  5017 | pg_mcv_list
-(5 rows)
+(4 rows)
 
 -- Make sure typarray points to a "true" array type of our own base
 SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 1c6bbfcf54..70c38309d7 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -77,11 +77,6 @@ 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_multi
-
 # ----------
 # Another group of parallel tests
 # ----------
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index dd3f3a9f0b..d81d04136c 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -108,7 +108,6 @@ test: delete
 test: namespace
 test: prepared_xacts
 test: brin
-test: brin_multi
 test: gin
 test: gist
 test: spgist
-- 
2.30.2

view thread (177+ messages)  latest in thread

Message-ID: <357fb6d7-649f-822e-ff43-16b39aed68ee@enterprisedb.com>
Permalink:  ../357fb6d7-649f-822e-ff43-16b39aed68ee@enterprisedb.com/
Also on:    postgresql.org/message-id/357fb6d7-649f-822e-ff43-16b39aed68ee@enterprisedb.com

 ·  · 

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@enterprisedb.com, john.naylor@enterprisedb.com, alvherre@alvh.no-ip.org, pgsql-hackers@lists.postgresql.org
  Subject: Re: WIP: BRIN multi-range indexes
  In-Reply-To: <357fb6d7-649f-822e-ff43-16b39aed68ee@enterprisedb.com>

* 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