public inbox for [email protected]
help / color / mirror / Atom feed[PATCH 5/9] BRIN minmax-multi indexes
7+ messages / 3 participants
[nested] [flat]
* [PATCH 5/9] BRIN minmax-multi indexes
@ 2021-02-03 18:00 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 7+ messages in thread
From: Tomas Vondra @ 2021-02-03 18:00 UTC (permalink / raw)
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 <[email protected]>
Reviewed-by: Alvaro Herrera <[email protected]>
Reviewed-by: Alexander Korotkov <[email protected]>
Reviewed-by: Sokolov Yura <[email protected]>
Discussion: https://postgr.es/m/[email protected]
Discussion: https://postgr.es/m/5d78b774-7e9c-c94e-12cf-fef51cc89b1a%402ndquadrant.com
---
doc/src/sgml/brin.sgml | 252 +-
doc/src/sgml/ref/create_index.sgml | 11 +
src/backend/access/brin/Makefile | 1 +
src/backend/access/brin/brin_minmax_multi.c | 2579 +++++++++++++++++++
src/backend/access/brin/brin_tuple.c | 18 +
src/include/access/brin.h | 1 +
src/include/access/brin_internal.h | 3 +
src/include/access/brin_tuple.h | 8 +
src/include/access/transam.h | 2 +-
src/include/catalog/pg_amop.dat | 544 ++++
src/include/catalog/pg_amproc.dat | 600 ++++-
src/include/catalog/pg_opclass.dat | 57 +
src/include/catalog/pg_opfamily.dat | 28 +
src/include/catalog/pg_proc.dat | 85 +
src/include/catalog/pg_type.dat | 6 +
src/test/regress/expected/brin_multi.out | 445 ++++
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 | 397 +++
21 files changed, 5041 insertions(+), 19 deletions(-)
create mode 100644 src/backend/access/brin/brin_minmax_multi.c
create mode 100644 src/test/regress/expected/brin_multi.out
create mode 100644 src/test/regress/sql/brin_multi.sql
diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml
index 577dd18c49..3caa30f104 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -214,6 +214,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (date,date)</literal></entry></row>
<row><entry><literal>>= (date,date)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry>
+ <entry><literal>= (date,date)</literal></entry>
+ </row>
+ <row><entry><literal>< (date,date)</literal></entry></row>
+ <row><entry><literal><= (date,date)</literal></entry></row>
+ <row><entry><literal>> (date,date)</literal></entry></row>
+ <row><entry><literal>>= (date,date)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>float4_bloom_ops</literal></entry>
<entry><literal>= (float4,float4)</literal></entry>
@@ -228,6 +237,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (float4,float4)</literal></entry></row>
<row><entry><literal>>= (float4,float4)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry>
+ <entry><literal>= (float4,float4)</literal></entry>
+ </row>
+ <row><entry><literal>< (float4,float4)</literal></entry></row>
+ <row><entry><literal>> (float4,float4)</literal></entry></row>
+ <row><entry><literal><= (float4,float4)</literal></entry></row>
+ <row><entry><literal>>= (float4,float4)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>float8_bloom_ops</literal></entry>
<entry><literal>= (float8,float8)</literal></entry>
@@ -242,6 +260,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (float8,float8)</literal></entry></row>
<row><entry><literal>>= (float8,float8)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry>
+ <entry><literal>= (float8,float8)</literal></entry>
+ </row>
+ <row><entry><literal>< (float8,float8)</literal></entry></row>
+ <row><entry><literal><= (float8,float8)</literal></entry></row>
+ <row><entry><literal>> (float8,float8)</literal></entry></row>
+ <row><entry><literal>>= (float8,float8)</literal></entry></row>
+
<row>
<entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry>
<entry><literal><< (inet,inet)</literal></entry>
@@ -266,6 +293,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (inet,inet)</literal></entry></row>
<row><entry><literal>>= (inet,inet)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry>
+ <entry><literal>= (inet,inet)</literal></entry>
+ </row>
+ <row><entry><literal>< (inet,inet)</literal></entry></row>
+ <row><entry><literal><= (inet,inet)</literal></entry></row>
+ <row><entry><literal>> (inet,inet)</literal></entry></row>
+ <row><entry><literal>>= (inet,inet)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>int2_bloom_ops</literal></entry>
<entry><literal>= (int2,int2)</literal></entry>
@@ -280,6 +316,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (int2,int2)</literal></entry></row>
<row><entry><literal>>= (int2,int2)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry>
+ <entry><literal>= (int2,int2)</literal></entry>
+ </row>
+ <row><entry><literal>< (int2,int2)</literal></entry></row>
+ <row><entry><literal>> (int2,int2)</literal></entry></row>
+ <row><entry><literal><= (int2,int2)</literal></entry></row>
+ <row><entry><literal>>= (int2,int2)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>int4_bloom_ops</literal></entry>
<entry><literal>= (int4,int4)</literal></entry>
@@ -294,6 +339,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (int4,int4)</literal></entry></row>
<row><entry><literal>>= (int4,int4)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry>
+ <entry><literal>= (int4,int4)</literal></entry>
+ </row>
+ <row><entry><literal>< (int4,int4)</literal></entry></row>
+ <row><entry><literal>> (int4,int4)</literal></entry></row>
+ <row><entry><literal><= (int4,int4)</literal></entry></row>
+ <row><entry><literal>>= (int4,int4)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>int8_bloom_ops</literal></entry>
<entry><literal>= (bigint,bigint)</literal></entry>
@@ -308,6 +362,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (bigint,bigint)</literal></entry></row>
<row><entry><literal>>= (bigint,bigint)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry>
+ <entry><literal>= (bigint,bigint)</literal></entry>
+ </row>
+ <row><entry><literal>< (bigint,bigint)</literal></entry></row>
+ <row><entry><literal>> (bigint,bigint)</literal></entry></row>
+ <row><entry><literal><= (bigint,bigint)</literal></entry></row>
+ <row><entry><literal>>= (bigint,bigint)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>interval_bloom_ops</literal></entry>
<entry><literal>= (interval,interval)</literal></entry>
@@ -322,6 +385,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (interval,interval)</literal></entry></row>
<row><entry><literal>>= (interval,interval)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry>
+ <entry><literal>= (interval,interval)</literal></entry>
+ </row>
+ <row><entry><literal>< (interval,interval)</literal></entry></row>
+ <row><entry><literal><= (interval,interval)</literal></entry></row>
+ <row><entry><literal>> (interval,interval)</literal></entry></row>
+ <row><entry><literal>>= (interval,interval)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>macaddr_bloom_ops</literal></entry>
<entry><literal>= (macaddr,macaddr)</literal></entry>
@@ -336,6 +408,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (macaddr,macaddr)</literal></entry></row>
<row><entry><literal>>= (macaddr,macaddr)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry>
+ <entry><literal>= (macaddr,macaddr)</literal></entry>
+ </row>
+ <row><entry><literal>< (macaddr,macaddr)</literal></entry></row>
+ <row><entry><literal><= (macaddr,macaddr)</literal></entry></row>
+ <row><entry><literal>> (macaddr,macaddr)</literal></entry></row>
+ <row><entry><literal>>= (macaddr,macaddr)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry>
<entry><literal>= (macaddr8,macaddr8)</literal></entry>
@@ -350,6 +431,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (macaddr8,macaddr8)</literal></entry></row>
<row><entry><literal>>= (macaddr8,macaddr8)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry>
+ <entry><literal>= (macaddr8,macaddr8)</literal></entry>
+ </row>
+ <row><entry><literal>< (macaddr8,macaddr8)</literal></entry></row>
+ <row><entry><literal><= (macaddr8,macaddr8)</literal></entry></row>
+ <row><entry><literal>> (macaddr8,macaddr8)</literal></entry></row>
+ <row><entry><literal>>= (macaddr8,macaddr8)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>name_bloom_ops</literal></entry>
<entry><literal>= (name,name)</literal></entry>
@@ -378,6 +468,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (numeric,numeric)</literal></entry></row>
<row><entry><literal>>= (numeric,numeric)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry>
+ <entry><literal>= (numeric,numeric)</literal></entry>
+ </row>
+ <row><entry><literal>< (numeric,numeric)</literal></entry></row>
+ <row><entry><literal><= (numeric,numeric)</literal></entry></row>
+ <row><entry><literal>> (numeric,numeric)</literal></entry></row>
+ <row><entry><literal>>= (numeric,numeric)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>oid_bloom_ops</literal></entry>
<entry><literal>= (oid,oid)</literal></entry>
@@ -392,6 +491,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (oid,oid)</literal></entry></row>
<row><entry><literal>>= (oid,oid)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry>
+ <entry><literal>= (oid,oid)</literal></entry>
+ </row>
+ <row><entry><literal>< (oid,oid)</literal></entry></row>
+ <row><entry><literal>> (oid,oid)</literal></entry></row>
+ <row><entry><literal><= (oid,oid)</literal></entry></row>
+ <row><entry><literal>>= (oid,oid)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry>
<entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
@@ -406,6 +514,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (pg_lsn,pg_lsn)</literal></entry></row>
<row><entry><literal>>= (pg_lsn,pg_lsn)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry>
+ <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
+ </row>
+ <row><entry><literal>< (pg_lsn,pg_lsn)</literal></entry></row>
+ <row><entry><literal>> (pg_lsn,pg_lsn)</literal></entry></row>
+ <row><entry><literal><= (pg_lsn,pg_lsn)</literal></entry></row>
+ <row><entry><literal>>= (pg_lsn,pg_lsn)</literal></entry></row>
+
<row>
<entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry>
<entry><literal>= (anyrange,anyrange)</literal></entry>
@@ -452,6 +569,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (tid,tid)</literal></entry></row>
<row><entry><literal>>= (tid,tid)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry>
+ <entry><literal>= (tid,tid)</literal></entry>
+ </row>
+ <row><entry><literal>< (tid,tid)</literal></entry></row>
+ <row><entry><literal>> (tid,tid)</literal></entry></row>
+ <row><entry><literal><= (tid,tid)</literal></entry></row>
+ <row><entry><literal>>= (tid,tid)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>timestamp_bloom_ops</literal></entry>
<entry><literal>= (timestamp,timestamp)</literal></entry>
@@ -466,6 +592,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timestamp,timestamp)</literal></entry></row>
<row><entry><literal>>= (timestamp,timestamp)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry>
+ <entry><literal>= (timestamp,timestamp)</literal></entry>
+ </row>
+ <row><entry><literal>< (timestamp,timestamp)</literal></entry></row>
+ <row><entry><literal><= (timestamp,timestamp)</literal></entry></row>
+ <row><entry><literal>> (timestamp,timestamp)</literal></entry></row>
+ <row><entry><literal>>= (timestamp,timestamp)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry>
<entry><literal>= (timestamptz,timestamptz)</literal></entry>
@@ -480,6 +615,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timestamptz,timestamptz)</literal></entry></row>
<row><entry><literal>>= (timestamptz,timestamptz)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry>
+ <entry><literal>= (timestamptz,timestamptz)</literal></entry>
+ </row>
+ <row><entry><literal>< (timestamptz,timestamptz)</literal></entry></row>
+ <row><entry><literal><= (timestamptz,timestamptz)</literal></entry></row>
+ <row><entry><literal>> (timestamptz,timestamptz)</literal></entry></row>
+ <row><entry><literal>>= (timestamptz,timestamptz)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>time_bloom_ops</literal></entry>
<entry><literal>= (time,time)</literal></entry>
@@ -494,6 +638,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (time,time)</literal></entry></row>
<row><entry><literal>>= (time,time)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry>
+ <entry><literal>= (time,time)</literal></entry>
+ </row>
+ <row><entry><literal>< (time,time)</literal></entry></row>
+ <row><entry><literal><= (time,time)</literal></entry></row>
+ <row><entry><literal>> (time,time)</literal></entry></row>
+ <row><entry><literal>>= (time,time)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>timetz_bloom_ops</literal></entry>
<entry><literal>= (timetz,timetz)</literal></entry>
@@ -508,6 +661,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal>> (timetz,timetz)</literal></entry></row>
<row><entry><literal>>= (timetz,timetz)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry>
+ <entry><literal>= (timetz,timetz)</literal></entry>
+ </row>
+ <row><entry><literal>< (timetz,timetz)</literal></entry></row>
+ <row><entry><literal><= (timetz,timetz)</literal></entry></row>
+ <row><entry><literal>> (timetz,timetz)</literal></entry></row>
+ <row><entry><literal>>= (timetz,timetz)</literal></entry></row>
+
<row>
<entry valign="middle"><literal>uuid_bloom_ops</literal></entry>
<entry><literal>= (uuid,uuid)</literal></entry>
@@ -522,6 +684,15 @@ LOG: request for BRIN range summarization for index "brin_wi_idx" page 128 was
<row><entry><literal><= (uuid,uuid)</literal></entry></row>
<row><entry><literal>>= (uuid,uuid)</literal></entry></row>
+ <row>
+ <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry>
+ <entry><literal>= (uuid,uuid)</literal></entry>
+ </row>
+ <row><entry><literal>< (uuid,uuid)</literal></entry></row>
+ <row><entry><literal>> (uuid,uuid)</literal></entry></row>
+ <row><entry><literal><= (uuid,uuid)</literal></entry></row>
+ <row><entry><literal>>= (uuid,uuid)</literal></entry></row>
+
<row>
<entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry>
<entry><literal>= (varbit,varbit)</literal></entry>
@@ -654,13 +825,14 @@ typedef struct BrinOpcInfo
</varlistentry>
</variablelist>
- The core distribution includes support for two types of operator classes:
- minmax and inclusion. Operator class definitions using them are shipped for
- in-core data types as appropriate. Additional operator classes can be
- defined by the user for other data types using equivalent definitions,
- without having to write any source code; appropriate catalog entries being
- declared is enough. Note that assumptions about the semantics of operator
- strategies are embedded in the support functions' source code.
+ The core distribution includes support for four types of operator classes:
+ minmax, multi-minmax, inclusion and bloom. Operator class definitions
+ using them are shipped for in-core data types as appropriate. Additional
+ operator classes can be defined by the user for other data types using
+ equivalent definitions, without having to write any source code;
+ appropriate catalog entries being declared is enough. Note that
+ assumptions about the semantics of operator strategies are embedded in the
+ support functions' source code.
</para>
<para>
@@ -957,6 +1129,72 @@ typedef struct BrinOpcInfo
and return a hash of the value.
</para>
+ <para>
+ The multi-minmax operator class is also intended for data types implementing
+ a totally ordered sets, and may be seen as a simple extension of the minmax
+ operator class. While minmax operator class summarizes values from each block
+ range into a single contiguous interval, multi-minmax allows summarization
+ into multiple smaller intervals to improve handling of outlier values.
+ It is possible to use the multi-minmax support procedures alongside the
+ corresponding operators, as shown in
+ <xref linkend="brin-extensibility-multi-minmax-table"/>.
+ All operator class members (procedures and operators) are mandatory.
+ </para>
+
+ <table id="brin-extensibility-multi-minmax-table">
+ <title>Procedure and Support Numbers for Multi-Minmax Operator Classes</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Operator class member</entry>
+ <entry>Object</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Support Procedure 1</entry>
+ <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 2</entry>
+ <entry>internal function <function>brin_minmax_multi_add_value()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 3</entry>
+ <entry>internal function <function>brin_minmax_multi_consistent()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 4</entry>
+ <entry>internal function <function>brin_minmax_multi_union()</function></entry>
+ </row>
+ <row>
+ <entry>Support Procedure 11</entry>
+ <entry>function to compute distance between two values (length of a range)</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 1</entry>
+ <entry>operator less-than</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 2</entry>
+ <entry>operator less-than-or-equal-to</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 3</entry>
+ <entry>operator equal-to</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 4</entry>
+ <entry>operator greater-than-or-equal-to</entry>
+ </row>
+ <row>
+ <entry>Operator Strategy 5</entry>
+ <entry>operator greater-than</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
<para>
Both minmax and inclusion operator classes support cross-data-type
operators, though with these the dependencies become more complicated.
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 940a0fd8fd..f8a82205d8 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -612,6 +612,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>values_per_range</literal></term>
+ <listitem>
+ <para>
+ Defines the maximum number of values stored by <acronym>BRIN</acronym>
+ minmax indexes to summarize a block range. Each value may represent
+ eiter a point, or boundary of an interval. Values must be be between
+ 8 and 256, and the default value is 32.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect2>
diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile
index 6b56131215..a386cb71f1 100644
--- a/src/backend/access/brin/Makefile
+++ b/src/backend/access/brin/Makefile
@@ -17,6 +17,7 @@ OBJS = \
brin_bloom.o \
brin_inclusion.o \
brin_minmax.o \
+ brin_minmax_multi.o \
brin_pageops.o \
brin_revmap.o \
brin_tuple.o \
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
new file mode 100644
index 0000000000..2f6e1793dc
--- /dev/null
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -0,0 +1,2579 @@
+/*
+ * brin_minmax_multi.c
+ * Implementation of Multi Min/Max opclass for BRIN
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * Implements a variant of minmax opclass, where the summary is composed of
+ * multiple smaller intervals. This allows us to handle outliers, which
+ * usually makes the simple minmax opclass inefficient.
+ *
+ * Consider for example page range with simple minmax interval [1000,2000],
+ * and assume a new row gets inserted into the range with value 1000000.
+ * Due to that the interval gets [1000,1000000]. I.e. the minmax interval
+ * got 1000x wider and won't be useful to eliminate scan keys between 2001
+ * and 1000000.
+ *
+ * With multi-minmax opclass, we may have [1000,2000] interval initially,
+ * but after adding the new row we start tracking it as two interval:
+ *
+ * [1000,2000] and [1000000,1000000]
+ *
+ * This allow us to still eliminate the page range when the scan keys hit
+ * the gap between 2000 and 1000000, making it useful in cases when the
+ * simple minmax opclass gets inefficient.
+ *
+ * The number of intervals tracked per page range is somewhat flexible.
+ * What is restricted is the number of values per page range, and the limit
+ * is currently 64 (see values_per_range reloption). Collapsed intervals
+ * (with equal minimum and maximum value) are stored as a single value,
+ * while regular intervals require two values.
+ *
+ * When the number of values gets too high (by adding new values to the
+ * summary), we merge some of the intervals to free space for more values.
+ * This is done in a greedy way - we simply pick the two closest intervals,
+ * merge them, and repeat this until the number of values to store gets
+ * sufficiently low (below 75% of maximum values), but that is mostly
+ * arbitrary threshold and may be changed easily).
+ *
+ * To pick the closest intervals we use the "distance" support procedure,
+ * which measures space between two ranges (i.e. length of an interval).
+ * The computed value may be an approximation - in the worst case we will
+ * merge two ranges that are slightly less optimal at that step, but the
+ * index should still produce correct results.
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/brin/brin_minmax_multi.c
+ */
+#include "postgres.h"
+
+/* 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_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
+
+#define MINMAX_LOAD_FACTOR 0.75
+
+typedef struct MinmaxMultiOpaque
+{
+ FmgrInfo extra_procinfos[MINMAX_MAX_PROCNUMS];
+ bool extra_proc_missing[MINMAX_MAX_PROCNUMS];
+ Oid cached_subtype;
+ FmgrInfo strategy_procinfos[BTMaxStrategyNumber];
+} MinmaxMultiOpaque;
+
+/*
+ * Storage type for BRIN's minmax reloptions
+ */
+typedef struct MinMaxOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int valuesPerRange; /* number of values per range */
+} MinMaxOptions;
+
+#define MINMAX_DEFAULT_VALUES_PER_PAGE 32
+
+#define MinMaxGetValuesPerRange(opts) \
+ ((opts) && (((MinMaxOptions *) (opts))->valuesPerRange != 0) ? \
+ ((MinMaxOptions *) (opts))->valuesPerRange : \
+ MINMAX_DEFAULT_VALUES_PER_PAGE)
+
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
+
+/*
+ * The summary of multi-minmax indexes has two representations - Ranges for
+ * convenient processing, and SerializedRanges for storage in bytea value.
+ *
+ * The Ranges struct stores the boundary values in a single array, but we
+ * treat regular and single-point ranges differently to save space. For
+ * regular ranges (with different boundary values) we have to store both
+ * values, while for "single-point ranges" we only need to save one value.
+ *
+ * The 'values' array stores boundary values for regular ranges first (there
+ * are 2*nranges values to store), and then the nvalues boundary values for
+ * single-point ranges. That is, we have (2*nranges + nvalues) boundary
+ * values in the array.
+ *
+ * +---------------------------------+-------------------------------+
+ * | ranges (sorted pairs of values) | sorted values (single points) |
+ * +---------------------------------+-------------------------------+
+ *
+ * This allows us to quickly add new values, and store outliers without
+ * making the other ranges very wide.
+ *
+ * We never store more than maxvalues values (as set by values_per_range
+ * reloption). If needed we merge some of the ranges.
+ *
+ * To minimize palloc overhead, we always allocate the full array with
+ * space for maxvalues elements. This should be fine as long as the
+ * maxvalues is reasonably small (64 seems fine), which is the case
+ * thanks to values_per_range reloption being limited to 256.
+ */
+typedef struct Ranges
+{
+ Oid typid;
+
+ /* (2*nranges + nvalues) <= maxvalues */
+ int nranges; /* number of ranges in the array (stored) */
+ int nvalues; /* number of values in the data array (all) */
+ int maxvalues; /* maximum number of values (reloption) */
+
+ /* values stored for this range - either raw values, or ranges */
+ Datum values[FLEXIBLE_ARRAY_MEMBER];
+} Ranges;
+
+/*
+ * On-disk the summary is stored as a bytea value, 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(SerializedRanges *range);
+
+/* Cache for support and strategy procesures. */
+
+static FmgrInfo *minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno,
+ uint16 procnum);
+
+static FmgrInfo *minmax_multi_get_strategy_procinfo(BrinDesc *bdesc,
+ uint16 attno, Oid subtype, uint16 strategynum);
+
+
+/*
+ * minmax_multi_init
+ * Initialize the deserialized range list, allocate all the memory.
+ *
+ * This is only in-memory representation of the ranges, so we allocate
+ * everything enough space for the maximum number of values (so as not
+ * to have to do repallocs as the ranges grow).
+ */
+static Ranges *
+minmax_multi_init(int maxvalues)
+{
+ Size len;
+ Ranges * ranges;
+
+ Assert(maxvalues > 0);
+
+ len = offsetof(Ranges, values); /* fixed header */
+ len += maxvalues * sizeof(Datum); /* Datum values */
+
+ ranges = (Ranges *) palloc0(len);
+
+ ranges->maxvalues = maxvalues;
+
+ return ranges;
+}
+
+/*
+ * range_serialize
+ * Serialize the in-memory representation into a compact varlena value.
+ *
+ * Simply copy the header and then also the individual values, as stored
+ * in the in-memory value array.
+ */
+static SerializedRanges *
+range_serialize(Ranges *range)
+{
+ Size len;
+ int nvalues;
+ SerializedRanges *serialized;
+ Oid typid;
+ int typlen;
+ bool typbyval;
+
+ int i;
+ char *ptr;
+
+ /* simple sanity checks */
+ Assert(range->nranges >= 0);
+ Assert(range->nvalues >= 0);
+ Assert(range->maxvalues > 0);
+
+ /* see how many Datum values we actually have */
+ nvalues = 2*range->nranges + range->nvalues;
+
+ Assert(2*range->nranges + range->nvalues <= range->maxvalues);
+
+ typid = range->typid;
+ typbyval = get_typbyval(typid);
+ typlen = get_typlen(typid);
+
+ /* header is always needed */
+ len = offsetof(SerializedRanges,data);
+
+ /*
+ * The space needed depends on data type - for fixed-length data types
+ * (by-value and some by-reference) it's pretty simple, just multiply
+ * (attlen * nvalues) and we're done. For variable-length by-reference
+ * types we need to actually walk all the values and sum the lengths.
+ */
+ if (typlen == -1) /* varlena */
+ {
+ int i;
+ for (i = 0; i < nvalues; i++)
+ {
+ len += VARSIZE_ANY(range->values[i]);
+ }
+ }
+ else if (typlen == -2) /* cstring */
+ {
+ int i;
+ for (i = 0; i < nvalues; i++)
+ {
+ /* don't forget to include the null terminator ;-) */
+ len += strlen(DatumGetPointer(range->values[i])) + 1;
+ }
+ }
+ else /* fixed-length types (even by-reference) */
+ {
+ Assert(typlen > 0);
+ len += nvalues * typlen;
+ }
+
+ /*
+ * Allocate the serialized object, copy the basic information. The
+ * serialized object is a varlena, so update the header.
+ */
+ serialized = (SerializedRanges *) palloc0(len);
+ SET_VARSIZE(serialized, len);
+
+ serialized->typid = typid;
+ serialized->nranges = range->nranges;
+ serialized->nvalues = range->nvalues;
+ serialized->maxvalues = range->maxvalues;
+
+ /*
+ * And now copy also the boundary values (like the length calculation
+ * this depends on the particular data type).
+ */
+ ptr = serialized->data; /* start of the serialized data */
+
+ for (i = 0; i < nvalues; i++)
+ {
+ if (typbyval) /* simple by-value data types */
+ {
+ memcpy(ptr, &range->values[i], typlen);
+ ptr += typlen;
+ }
+ else if (typlen > 0) /* fixed-length by-ref types */
+ {
+ memcpy(ptr, DatumGetPointer(range->values[i]), typlen);
+ ptr += typlen;
+ }
+ else if (typlen == -1) /* varlena */
+ {
+ int tmp = VARSIZE_ANY(DatumGetPointer(range->values[i]));
+ memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
+ ptr += tmp;
+ }
+ else if (typlen == -2) /* cstring */
+ {
+ int tmp = strlen(DatumGetPointer(range->values[i])) + 1;
+ memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
+ ptr += tmp;
+ }
+
+ /* make sure we haven't overflown the buffer end */
+ Assert(ptr <= ((char *)serialized + len));
+ }
+
+ /* exact size */
+ Assert(ptr == ((char *)serialized + len));
+
+ return serialized;
+}
+
+/*
+ * range_deserialize
+ * Serialize the in-memory representation into a compact varlena value.
+ *
+ * Simply copy the header and then also the individual values, as stored
+ * in the in-memory value array.
+ */
+static Ranges *
+range_deserialize(SerializedRanges *serialized)
+{
+ int i,
+ nvalues;
+ char *ptr;
+ bool typbyval;
+ int typlen;
+
+ Ranges *range;
+
+ Assert(serialized->nranges >= 0);
+ Assert(serialized->nvalues >= 0);
+ Assert(serialized->maxvalues > 0);
+
+ nvalues = 2*serialized->nranges + serialized->nvalues;
+
+ Assert(nvalues <= serialized->maxvalues);
+
+ range = minmax_multi_init(serialized->maxvalues);
+
+ /* copy the header info */
+ range->nranges = serialized->nranges;
+ range->nvalues = serialized->nvalues;
+ range->maxvalues = serialized->maxvalues;
+ range->typid = serialized->typid;
+
+ typbyval = get_typbyval(serialized->typid);
+ typlen = get_typlen(serialized->typid);
+
+ /*
+ * And now deconstruct the values into Datum array. We don't need
+ * to copy the values and will instead just point the values to the
+ * serialized varlena value (assuming it will be kept around).
+ */
+ ptr = serialized->data;
+
+ for (i = 0; i < nvalues; i++)
+ {
+ if (typbyval) /* simple by-value data types */
+ {
+ memcpy(&range->values[i], ptr, typlen);
+ ptr += typlen;
+ }
+ else if (typlen > 0) /* fixed-length by-ref types */
+ {
+ /* no copy, just set the value to the pointer */
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += typlen;
+ }
+ else if (typlen == -1) /* varlena */
+ {
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += VARSIZE_ANY(DatumGetPointer(range->values[i]));
+ }
+ else if (typlen == -2) /* cstring */
+ {
+ range->values[i] = PointerGetDatum(ptr);
+ ptr += strlen(DatumGetPointer(range->values[i])) + 1;
+ }
+
+ /* make sure we haven't overflown the buffer end */
+ Assert(ptr <= ((char *)serialized + VARSIZE_ANY(serialized)));
+ }
+
+ /* should have consumed the whole input value exactly */
+ Assert(ptr == ((char *)serialized + VARSIZE_ANY(serialized)));
+
+ /* return the deserialized value */
+ return range;
+}
+
+typedef struct compare_context
+{
+ FmgrInfo *cmpFn;
+ Oid colloid;
+} compare_context;
+
+/*
+ * Used to represent ranges expanded during merging and combining (to
+ * reduce number of boundary values to store).
+ *
+ * XXX CombineRange name seems a bit weird. Consider renaming, perhaps to
+ * something ExpandedRange or so.
+ */
+typedef struct CombineRange
+{
+ Datum minval; /* lower boundary */
+ Datum maxval; /* upper boundary */
+ bool collapsed; /* true if minval==maxval */
+} CombineRange;
+
+/*
+ * compare_combine_ranges
+ * Compare the combine ranges - first by minimum, then by maximum.
+ *
+ * We do guarantee that ranges in a single Range object do not overlap,
+ * so it may seem strange that we don't order just by minimum. But when
+ * merging two Ranges (which happens in the union function), the ranges
+ * may in fact overlap. So we do compare both.
+ */
+static int
+compare_combine_ranges(const void *a, const void *b, void *arg)
+{
+ CombineRange *ra = (CombineRange *)a;
+ CombineRange *rb = (CombineRange *)b;
+ Datum r;
+
+ compare_context *cxt = (compare_context *)arg;
+
+ /* first compare minvals */
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, ra->minval, rb->minval);
+
+ if (DatumGetBool(r))
+ return -1;
+
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, rb->minval, ra->minval);
+
+ if (DatumGetBool(r))
+ return 1;
+
+ /* then compare maxvals */
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, ra->maxval, rb->maxval);
+
+ if (DatumGetBool(r))
+ return -1;
+
+ r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, rb->maxval, ra->maxval);
+
+ if (DatumGetBool(r))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * range_contains_value
+ * See if the new value is already contained in the range list.
+ *
+ * We first inspect the list of intervals. We use a small trick - we check
+ * the value against min/max of the whole range (min of the first interval,
+ * max of the last one) first, and only inspect the individual intervals if
+ * this passes.
+ *
+ * If the value matches none of the intervals, we check the exact values.
+ * We simply loop through them and invoke equality operator on them.
+ *
+ * XXX This might benefit from the fact that both the intervals and exact
+ * values are sorted - we might do bsearch or something. Currently that
+ * does not make much difference (there are only ~32 intervals), but if
+ * this gets increased and/or the comparator function is more expensive,
+ * it might be a huge win.
+ */
+static bool
+range_contains_value(BrinDesc *bdesc, Oid colloid,
+ AttrNumber attno, Form_pg_attribute attr,
+ Ranges *ranges, Datum newval)
+{
+ int i;
+ FmgrInfo *cmpLessFn;
+ FmgrInfo *cmpGreaterFn;
+ FmgrInfo *cmpEqualFn;
+ Oid typid = attr->atttypid;
+
+ /*
+ * First inspect the ranges, if there are any. We first check the whole
+ * range, and only when there's still a chance of getting a match we
+ * inspect the individual ranges.
+ */
+ if (ranges->nranges > 0)
+ {
+ Datum compar;
+ bool match = true;
+
+ Datum minvalue = ranges->values[0];
+ Datum maxvalue = ranges->values[2*ranges->nranges - 1];
+
+ /*
+ * Otherwise, need to compare the new value with boundaries of all
+ * the ranges. First check if it's less than the absolute minimum,
+ * which is the first value in the array.
+ */
+ cmpLessFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTLessStrategyNumber);
+ compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue);
+
+ /* smaller than the smallest value in the range list */
+ if (DatumGetBool(compar))
+ match = false;
+
+ /*
+ * And now compare it to the existing maximum (last value in the
+ * data array). But only if we haven't already ruled out a possible
+ * match in the minvalue check.
+ */
+ if (match)
+ {
+ cmpGreaterFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTGreaterStrategyNumber);
+ compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue);
+
+ if (DatumGetBool(compar))
+ match = false;
+ }
+
+ /*
+ * So it's in the general range, but is it actually covered by any
+ * of the ranges? Repeat the check for each range.
+ *
+ * XXX We simply walk the ranges sequentially, but maybe we could
+ * further leverage the ordering and non-overlap and use bsearch to
+ * speed this up a bit.
+ */
+ for (i = 0; i < ranges->nranges && match; i++)
+ {
+ /* copy the min/max values from the ranges */
+ minvalue = ranges->values[2*i];
+ maxvalue = ranges->values[2*i+1];
+
+ /*
+ * Otherwise, need to compare the new value with boundaries of all
+ * the ranges. First check if it's less than the absolute minimum,
+ * which is the first value in the array.
+ */
+ compar = FunctionCall2Coll(cmpLessFn, colloid, newval, minvalue);
+
+ /* smaller than the smallest value in this range */
+ if (DatumGetBool(compar))
+ continue;
+
+ compar = FunctionCall2Coll(cmpGreaterFn, colloid, newval, maxvalue);
+
+ /* larger than the largest value in this range */
+ if (DatumGetBool(compar))
+ continue;
+
+ /* hey, we found a matching row */
+ return true;
+ }
+ }
+
+ cmpEqualFn = minmax_multi_get_strategy_procinfo(bdesc, attno, typid,
+ BTEqualStrategyNumber);
+
+ /*
+ * We're done with the ranges, now let's inspect the exact values.
+ *
+ * XXX Again, we do sequentially search the values - consider leveraging
+ * the ordering of values to improve performance.
+ */
+ for (i = 2*ranges->nranges; i < 2*ranges->nranges + ranges->nvalues; i++)
+ {
+ Datum compar;
+
+ compar = FunctionCall2Coll(cmpEqualFn, colloid, newval, ranges->values[i]);
+
+ /* found an exact match */
+ if (DatumGetBool(compar))
+ return true;
+ }
+
+ /* the value is not covered by this BRIN tuple */
+ return false;
+}
+
+/*
+ * insert_value
+ * Adds a new value into the single-point part, while maintaining ordering.
+ *
+ * The function inserts the new value to the right place in the single-point
+ * part of the range. It assumes there's enough free space, and then does
+ * essentially an insert-sort.
+ *
+ * XXX Assumes the 'values' array has space for (nvalues+1) entries, and that
+ * only the first nvalues are used.
+ */
+static void
+insert_value(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues,
+ Datum newvalue)
+{
+ int i;
+ Datum lt;
+
+ /* If there are no values yet, store the new one and we're done. */
+ if (!nvalues)
+ {
+ values[0] = newvalue;
+ return;
+ }
+
+ /*
+ * A common case is that the new value is entirely out of the existing
+ * range, i.e. it's either smaller or larger than all previous values.
+ * So we check and handle this case first - first we check the larger
+ * case, because in that case we can just append the value to the end
+ * of the array and we're done.
+ */
+
+ /* Is it greater than all existing values in the array? */
+ lt = FunctionCall2Coll(cmp, colloid, values[nvalues-1], newvalue);
+ if (DatumGetBool(lt))
+ {
+ /* just copy it in-place and we're done */
+ values[nvalues] = newvalue;
+ return;
+ }
+
+ /*
+ * OK, I lied a bit - we won't check the smaller case explicitly, but
+ * we'll just compare the value to all existing values in the array.
+ * But we happen to start with the smallest value, so we're actually
+ * doing the check anyway.
+ *
+ * XXX We do walk the values sequentially. Perhaps we could/should be
+ * smarter and do some sort of bisection, to improve performance?
+ */
+ for (i = 0; i < nvalues; i++)
+ {
+ lt = FunctionCall2Coll(cmp, colloid, newvalue, values[i]);
+ if (DatumGetBool(lt))
+ {
+ /*
+ * Move values to make space for the new entry, which should go
+ * to index 'i'. Entries 0 ... (i-1) should stay where they are.
+ */
+ memmove(&values[i+1], &values[i], (nvalues-i) * sizeof(Datum));
+ values[i] = newvalue;
+ return;
+ }
+ }
+
+ /* We should never really get here. */
+ Assert(false);
+}
+
+#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, j;
+
+ /* some basic sanity checks */
+ Assert(ranges->nranges >= 0);
+ Assert(ranges->nvalues >= 0);
+ 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 boundary values ) */
+ AssertArrayOrder(cmpFn, colloid, &ranges->values[2*ranges->nranges],
+ ranges->nvalues);
+
+ /* finally check that none of the values are not covered by ranges */
+ for (i = 0; i < ranges->nvalues; i++)
+ {
+ Datum value = ranges->values[2 * ranges->nranges + i];
+
+ for (j = 0; j < ranges->nranges; j++)
+ {
+ Datum r;
+
+ Datum minval = ranges->values[2 * j];
+ Datum maxval = ranges->values[2 * j + 1];
+
+ /* if value is smaller than range minimum, that's OK */
+ r = FunctionCall2Coll(cmpFn, colloid, value, minval);
+ if (DatumGetBool(r))
+ continue;
+
+ /* if value is greater than range maximum, that's OK */
+ r = FunctionCall2Coll(cmpFn, colloid, maxval, value);
+ if (DatumGetBool(r))
+ continue;
+
+ /* value is between [min,max], which is wrong */
+ Assert(false);
+ }
+ }
+#endif
+}
+
+/*
+ * Check that the expanded ranges (built when reducing the number of ranges
+ * by combining some of them) are correctly sorted and do not overlap.
+ */
+static void
+AssertValidCombineRanges(BrinDesc *bdesc, Oid colloid, AttrNumber attno,
+ Form_pg_attribute attr, CombineRange *ranges,
+ int nranges)
+{
+#ifdef USE_ASSERT_CHECKING
+ int i;
+ FmgrInfo *eq;
+ FmgrInfo *lt;
+
+ eq = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTEqualStrategyNumber);
+
+ lt = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /*
+ * Each range independently should be valid, i.e. that for the boundary
+ * values (lower <= upper).
+ */
+ for (i = 0; i < nranges; i++)
+ {
+ Datum r;
+ Datum minval = ranges[i].minval;
+ Datum maxval = ranges[i].maxval;
+
+ if (ranges[i].collapsed) /* collapsed: minval == maxval */
+ r = FunctionCall2Coll(eq, colloid, minval, maxval);
+ else /* non-collapsed: minval < maxval */
+ r = FunctionCall2Coll(lt, colloid, minval, maxval);
+
+ Assert(DatumGetBool(r));
+ }
+
+ /*
+ * And the ranges should be ordered and must nor overlap, i.e.
+ * upper < lower for boundaries of consecutive ranges.
+ */
+ for (i = 0; i < nranges-1; i++)
+ {
+ Datum r;
+ Datum maxval = ranges[i].maxval;
+ Datum minval = ranges[i+1].minval;
+
+ r = FunctionCall2Coll(lt, colloid, maxval, minval);
+
+ Assert(DatumGetBool(r));
+ }
+#endif
+}
+
+/*
+ * Expand ranges from Ranges into CombineRange array. This expects the
+ * cranges to be pre-allocated and sufficiently large (there needs to be
+ * at least (nranges + nvalues) slots).
+ */
+static void
+fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
+{
+ int idx;
+ int i;
+
+ idx = 0;
+ for (i = 0; i < ranges->nranges; i++)
+ {
+ cranges[idx].minval = ranges->values[2*i];
+ cranges[idx].maxval = ranges->values[2*i+1];
+ cranges[idx].collapsed = false;
+ idx++;
+
+ Assert(idx <= ncranges);
+ }
+
+ for (i = 0; i < ranges->nvalues; i++)
+ {
+ cranges[idx].minval = ranges->values[2*ranges->nranges + i];
+ cranges[idx].maxval = ranges->values[2*ranges->nranges + i];
+ cranges[idx].collapsed = true;
+ idx++;
+
+ Assert(idx <= ncranges);
+ }
+
+ Assert(idx == ncranges);
+
+ return;
+}
+
+/*
+ * Sort combine ranges using qsort (with BTLessStrategyNumber function).
+ */
+static void
+sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
+ CombineRange *cranges, int ncranges)
+{
+ compare_context cxt;
+
+ /* sort the values */
+ cxt.colloid = colloid;
+ cxt.cmpFn = cmp;
+
+ qsort_arg(cranges, ncranges, sizeof(CombineRange),
+ compare_combine_ranges, (void *) &cxt);
+}
+
+/*
+ * When combining multiple Range values (in union function), some of the
+ * ranges may overlap. We simply merge the overlapping ranges to fix that.
+ *
+ * XXX This assumes the combine ranges were previously sorted (by minval
+ * and then maxval). We leverage this when detecting overlap.
+ */
+static int
+merge_combine_ranges(FmgrInfo *cmp, Oid colloid,
+ CombineRange *cranges, int ncranges)
+{
+ int idx;
+
+ /* TODO: add assert checking the ordering of input ranges */
+
+ /* try merging ranges (idx) and (idx+1) if they overlap */
+ idx = 0;
+ while (idx < (ncranges-1))
+ {
+ Datum r;
+
+ /*
+ * comparing [?,maxval] vs. [minval,?] - the ranges overlap
+ * if (minval < maxval)
+ */
+ r = FunctionCall2Coll(cmp, colloid,
+ cranges[idx].maxval,
+ cranges[idx+1].minval);
+
+ /*
+ * Nope, maxval < minval, so no overlap. And we know the ranges
+ * are ordered, so there are no more overlaps, because all the
+ * remaining ranges have greater or equal minval.
+ */
+ if (DatumGetBool(r))
+ {
+ /* proceed to the next range */
+ idx += 1;
+ continue;
+ }
+
+ /*
+ * So ranges 'idx' and 'idx+1' do overlap, but we don't know if
+ * 'idx+1' is contained in 'idx', or if they only overlap only
+ * partially. So compare the upper bounds and keep the larger one.
+ */
+ r = FunctionCall2Coll(cmp, colloid,
+ cranges[idx].maxval,
+ cranges[idx+1].maxval);
+
+ if (DatumGetBool(r))
+ cranges[idx].maxval = cranges[idx+1].maxval;
+
+ /*
+ * The range certainly is no longer collapsed (irrespectedly of
+ * the previous state).
+ */
+ cranges[idx].collapsed = false;
+
+ /*
+ * Now get rid of the (idx+1) range entirely by shifting the
+ * remaining ranges by 1. There are ncranges elements, and we
+ * need to move elements from (idx+2). That means the number
+ * of elements to move is [ncranges - (idx+2)].
+ */
+ memmove(&cranges[idx+1], &cranges[idx+2],
+ (ncranges - (idx + 2)) * sizeof(CombineRange));
+
+ /*
+ * Decrease the number of ranges, and repeat (with the same range,
+ * as it might overlap with additional ranges thanks to the merge).
+ */
+ ncranges--;
+ }
+
+ /* TODO: add assert checking the ordering etc. of produced ranges */
+
+ return ncranges;
+}
+
+/*
+ * Represents a distance between two ranges (identified by index into
+ * an array of combine ranges).
+ */
+typedef struct DistanceValue
+{
+ int index;
+ double value;
+} DistanceValue;
+
+/*
+ * Simple comparator for distance values, comparing the double value.
+ * 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 combine ranges, compute distance of the gaps betwen
+ * the ranges - for ncranges there are (ncranges-1) gaps.
+ *
+ * We simply call the "distance" function to compute the (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_combine_ranges for details.
+ */
+static DistanceValue *
+build_distances(FmgrInfo *distanceFn, Oid colloid,
+ CombineRange *cranges, int ncranges)
+{
+ int i;
+ DistanceValue *distances;
+
+ Assert(ncranges >= 2);
+
+ distances = (DistanceValue *) palloc0(sizeof(DistanceValue) * (ncranges - 1));
+
+ /*
+ * Walk though the ranges once and compute distance between the ranges
+ * so that we can sort them once.
+ */
+ for (i = 0; i < (ncranges-1); i++)
+ {
+ Datum a1, a2, r;
+
+ a1 = cranges[i].maxval;
+ a2 = cranges[i+1].minval;
+
+ /* compute length of the 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, (ncranges-1), sizeof(DistanceValue),
+ compare_distances);
+
+ return distances;
+}
+
+/*
+ * Builds combine ranges for the existing ranges (and single-point ranges),
+ * and also the new value (which did not fit into the array).
+ *
+ * XXX We do perform qsort on all the values, but we could also leverage
+ * the fact that the input data is already sorted and do merge sort.
+ */
+static CombineRange *
+build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
+ Datum newvalue, int *nranges)
+{
+ int ncranges;
+ CombineRange *cranges;
+
+ /* now do the actual merge sort */
+ ncranges = ranges->nranges + ranges->nvalues + 1;
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
+ *nranges = ncranges;
+
+ /* put the new value at the beginning */
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
+
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+
+ /* and sort the ranges */
+ sort_combine_ranges(cmp, colloid, cranges, ncranges);
+
+ return cranges;
+}
+
+#ifdef USE_ASSERT_CHECKING
+/*
+ * Counts bondary values needed to store the ranges. Each single-point
+ * range is stored using a single value, each regular range needs two.
+ */
+static int
+count_values(CombineRange *cranges, int ncranges)
+{
+ int i;
+ int count;
+
+ count = 0;
+ for (i = 0; i < ncranges; i++)
+ {
+ if (cranges[i].collapsed)
+ count += 1;
+ else
+ count += 2;
+ }
+
+ return count;
+}
+#endif
+
+/*
+ * reduce_combine_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_combine_ranges(CombineRange *cranges, int ncranges,
+ 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 = (ncranges - 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 ncranges;
+
+ /* 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++] = cranges[0].minval;
+ values[nvalues++] = cranges[ncranges-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) < ncranges));
+
+ /* add max from the preceding range, minval from the next one */
+ values[nvalues++] = cranges[index].maxval;
+ values[nvalues++] = cranges[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++)
+ {
+ cranges[i].minval = values[2*i];
+ cranges[i].maxval = values[2*i + 1];
+
+ /* if the boundary values are the same, it's a collapsed range */
+ cranges[i].collapsed = (compare_values(&values[2*i],
+ &values[2*i+1],
+ &cxt) == 0);
+ }
+
+ return (nvalues / 2);
+}
+
+/*
+ * Store the boundary values from CombineRanges back into Range (using
+ * only the minimal number of values needed).
+ */
+static void
+store_combine_ranges(Ranges *ranges, CombineRange *cranges, int ncranges)
+{
+ int i;
+ int idx = 0;
+
+ /* first copy in the regular ranges */
+ ranges->nranges = 0;
+ for (i = 0; i < ncranges; i++)
+ {
+ if (!cranges[i].collapsed)
+ {
+ ranges->values[idx++] = cranges[i].minval;
+ ranges->values[idx++] = cranges[i].maxval;
+ ranges->nranges++;
+ }
+ }
+
+ /* now copy in the collapsed ones */
+ ranges->nvalues = 0;
+ for (i = 0; i < ncranges; i++)
+ {
+ if (cranges[i].collapsed)
+ {
+ ranges->values[idx++] = cranges[i].minval;
+ ranges->nvalues++;
+ }
+ }
+
+ Assert(count_values(cranges, ncranges) == 2*ranges->nranges + ranges->nvalues);
+ Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+}
+
+/*
+ * range_add_value
+ * Add the new value to the multi-minmax range.
+ */
+static bool
+range_add_value(BrinDesc *bdesc, Oid colloid,
+ AttrNumber attno, Form_pg_attribute attr,
+ Ranges *ranges, Datum newval)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /* 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);
+
+ /*
+ * Bail out if the value already is covered by the range.
+ *
+ * We could also add values until we hit values_per_range, and then
+ * do the deduplication in a batch, hoping for better efficiency. But
+ * that would mean we actually modify the range every time, which means
+ * having to serialize the value, which does palloc, walks the values,
+ * copies them, etc. Not exactly cheap.
+ *
+ * So instead we do the check, which should be fairly cheap - assuming
+ * the comparator function is not very expensive.
+ *
+ * This also implies means the values array can't contain duplicities.
+ */
+ if (range_contains_value(bdesc, colloid, attno, attr, ranges, newval))
+ return false;
+
+ /* 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.
+ */
+ if (2*ranges->nranges + ranges->nvalues < ranges->maxvalues)
+ {
+ Datum *values;
+
+ /* beginning of the 'single value' part (for convenience) */
+ values = &ranges->values[2*ranges->nranges];
+
+ insert_value(cmpFn, colloid, values, ranges->nvalues, newval);
+
+ ranges->nvalues++;
+
+ /*
+ * Check we haven't broken the ordering of boundary values (checks
+ * both parts, but that doesn't hurt).
+ */
+ AssertCheckRanges(ranges, cmpFn, colloid);
+
+ /* Also check the range contains the value we just added. */
+ // FIXME Assert(ranges, cmpFn, colloid);
+
+ /* yep, we've modified the range */
+ return true;
+ }
+
+ /*
+ * Damn - the new value is not in the range yet, but we don't have space
+ * to just insert it. So we need to combine some of the existing ranges,
+ * to reduce the number of values we need to store (joining two intervals
+ * reduces the number of boundaries to store by 2).
+ *
+ * To do that we first construct an array of CombineRange items - each
+ * combine range tracks if it's a regular range or collapsed range, where
+ * "collapsed" means "single point."
+ *
+ * Existing ranges (we have ranges->nranges of them) map to combine ranges
+ * directly, while single points (ranges->nvalues of them) have to be
+ * expanded. We neet the combine ranges to be sorted, and we do that by
+ * performing a merge sort of ranges, values and new value.
+ *
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+
+ /* 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, cranges, ncranges);
+
+ /*
+ * Combine ranges until we release at least 25% of the space. This
+ * threshold is somewhat arbitrary, perhaps needs tuning. We must not
+ * use too low or high value.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ ranges->maxvalues * MINMAX_LOAD_FACTOR,
+ cmpFn, colloid);
+
+ Assert(count_values(cranges, ncranges) <= ranges->maxvalues * MINMAX_LOAD_FACTOR);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Did we break the ranges somehow? */
+ AssertCheckRanges(ranges, cmpFn, colloid);
+ // FIXME Assert(ranges, cmpFn, colloid);
+
+ return true;
+}
+
+Datum
+brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
+{
+ BrinOpcInfo *result;
+
+ /*
+ * opaque->strategy_procinfos is initialized lazily; here it is set to
+ * all-uninitialized by palloc0 which sets fn_oid to InvalidOid.
+ */
+
+ result = palloc0(MAXALIGN(SizeofBrinOpcInfo(1)) +
+ sizeof(MinmaxMultiOpaque));
+ result->oi_nstored = 1;
+ result->oi_regular_nulls = true;
+ result->oi_opaque = (MinmaxMultiOpaque *)
+ MAXALIGN((char *) result + SizeofBrinOpcInfo(1));
+ result->oi_typcache[0] = lookup_type_cache(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);
+}
+
+/*
+ * Comutes distance between two numeric values (plain subtraction).
+ */
+Datum
+brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS)
+{
+ Datum d;
+ Datum a1 = PG_GETARG_DATUM(0);
+ Datum a2 = PG_GETARG_DATUM(1);
+
+ /*
+ * We know the values are range boundaries, but the range may be
+ * collapsed (i.e. single points), with equal values.
+ */
+ Assert(DatumGetBool(DirectFunctionCall2(numeric_le, a1, a2)));
+
+ d = DirectFunctionCall2(numeric_sub, a2, a1); /* a2 - a1 */
+
+ PG_RETURN_FLOAT8(DirectFunctionCall1(numeric_float8, d));
+}
+
+/*
+ * Computes approximate distance between two UUID values.
+ *
+ * XXX We do not need a perfectly accurate value, so we approximate the
+ * deltas (which would have to be 128-bit integers) with a 64-bit float.
+ * The small inaccuracies do not matter in practice, in the worst case
+ * we'll decide to merge ranges that are not the closest ones.
+ */
+Datum
+brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS)
+{
+ int i;
+ 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);
+
+ 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.
+ *
+ * XXX Does this need to consider the time zones?
+ */
+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 difference families are consider to be in maximum
+ * distance, which is 1.0.
+ *
+ * XXX Does this need to consider the mask (bits)? For now it's ignored.
+ */
+Datum
+brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
+{
+ 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 = range_serialize(ranges);
+ dst[0] = PointerGetDatum(s);
+}
+
+static int
+brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxOptions *opts)
+{
+ return MinMaxGetValuesPerRange(opts);
+}
+
+/*
+ * Examine the given index tuple (which contains partial status of a certain
+ * page range) by comparing it to the given value that comes from another heap
+ * tuple. If the new value is outside the min/max range specified by the
+ * existing tuple values, update the index tuple and return true. Otherwise,
+ * return false and do not modify in this case.
+ */
+Datum
+brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
+{
+ BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+ BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
+ Datum newval = PG_GETARG_DATUM(2);
+ bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3);
+ MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION();
+ bool modified = false;
+ Form_pg_attribute attr;
+ AttrNumber attno;
+ Ranges *ranges;
+ SerializedRanges *serialized = NULL;
+
+ Assert(!isnull);
+
+ attno = column->bv_attno;
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+
+ /* use the already deserialized value, is possible */
+ ranges = (Ranges *) DatumGetPointer(column->bv_mem_value);
+
+ /*
+ * If this is the first non-null value, we need to initialize the range
+ * list. Otherwise just extract the existing range list from BrinValues.
+ */
+ if (column->bv_allnulls)
+ {
+ MemoryContext oldctx;
+
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+
+ ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ ranges->typid = attr->atttypid;
+
+ MemoryContextSwitchTo(oldctx);
+
+ column->bv_allnulls = false;
+ modified = true;
+
+ column->bv_mem_value = PointerGetDatum(ranges);
+ column->bv_serialize = brin_minmax_multi_serialize;
+ }
+ else if (!ranges)
+ {
+ MemoryContext oldctx;
+
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+
+ serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+ ranges = range_deserialize(serialized);
+
+ column->bv_mem_value = PointerGetDatum(ranges);
+ column->bv_serialize = brin_minmax_multi_serialize;
+
+ MemoryContextSwitchTo(oldctx);
+ }
+
+ /*
+ * Try to add the new value to the range. We need to update the modified
+ * flag, so that we serialize the 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);
+ // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION(),
+ subtype;
+ AttrNumber attno;
+ Datum value;
+ FmgrInfo *finfo;
+ SerializedRanges *serialized;
+ Ranges *ranges;
+ int keyno;
+ int rangeno;
+ int i;
+
+ attno = column->bv_attno;
+
+ serialized = (SerializedRanges *) PG_DETOAST_DATUM(column->bv_values[0]);
+ ranges = range_deserialize(serialized);
+
+ /* inspect the ranges, and for each one evaluate the scan keys */
+ for (rangeno = 0; rangeno < ranges->nranges; rangeno++)
+ {
+ Datum minval = ranges->values[2*rangeno];
+ Datum maxval = ranges->values[2*rangeno+1];
+
+ /* assume the range is matching, and we'll try to prove otherwise */
+ bool matching = true;
+
+ for (keyno = 0; keyno < nkeys; keyno++)
+ {
+ Datum matches;
+ ScanKey key = keys[keyno];
+
+ /* NULL keys are handled and filtered-out in bringetbitmap */
+ Assert(!(key->sk_flags & SK_ISNULL));
+
+ attno = key->sk_attno;
+ subtype = key->sk_subtype;
+ value = key->sk_argument;
+ switch (key->sk_strategy)
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ key->sk_strategy);
+ /* first value from the array */
+ matches = FunctionCall2Coll(finfo, colloid, minval, value);
+ break;
+
+ case BTEqualStrategyNumber:
+ {
+ Datum compar;
+ FmgrInfo *cmpFn;
+
+ /* by default this range does not match */
+ matches = false;
+
+ /*
+ * Otherwise, need to compare the new value with boundaries of all
+ * the ranges. First check if it's less than the absolute minimum,
+ * which is the first value in the array.
+ */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ BTLessStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, colloid, value, minval);
+
+ /* smaller than the smallest value in this range */
+ if (DatumGetBool(compar))
+ break;
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ BTGreaterStrategyNumber);
+ compar = FunctionCall2Coll(cmpFn, colloid, value, maxval);
+
+ /* larger than the largest value in this range */
+ if (DatumGetBool(compar))
+ break;
+
+ /* haven't managed to eliminate this range, so consider it matching */
+ matches = true;
+
+ break;
+ }
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ key->sk_strategy);
+ /* last value from the array */
+ matches = FunctionCall2Coll(finfo, colloid, maxval, value);
+ break;
+
+ default:
+ /* shouldn't happen */
+ elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+ matches = 0;
+ break;
+ }
+
+ /* the range has to match all the scan keys */
+ matching &= DatumGetBool(matches);
+
+ /* once we find a non-matching key, we're done */
+ if (! matching)
+ break;
+ }
+
+ /* have we found a range matching all scan keys? if yes, we're
+ * done */
+ if (matching)
+ PG_RETURN_DATUM(BoolGetDatum(true));
+ }
+
+ /* and now inspect the values */
+ for (i = 0; i < ranges->nvalues; i++)
+ {
+ Datum val = ranges->values[2*ranges->nranges + i];
+
+ /* assume the range is matching, and we'll try to prove otherwise */
+ bool matching = true;
+
+ for (keyno = 0; keyno < nkeys; keyno++)
+ {
+ Datum matches;
+ ScanKey key = keys[keyno];
+
+ /* we've already dealt with NULL keys at the beginning */
+ if (key->sk_flags & SK_ISNULL)
+ continue;
+
+ attno = key->sk_attno;
+ subtype = key->sk_subtype;
+ value = key->sk_argument;
+ switch (key->sk_strategy)
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ case BTEqualStrategyNumber:
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+
+ finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
+ key->sk_strategy);
+ matches = FunctionCall2Coll(finfo, colloid, val, value);
+ break;
+
+ default:
+ /* shouldn't happen */
+ elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+ matches = 0;
+ break;
+ }
+
+ /* the range has to match all the scan keys */
+ matching &= DatumGetBool(matches);
+
+ /* once we find a non-matching key, we're done */
+ if (! matching)
+ break;
+ }
+
+ /* have we found a range matching all scan keys? if yes, we're
+ * done */
+ if (matching)
+ PG_RETURN_DATUM(BoolGetDatum(true));
+ }
+
+ PG_RETURN_DATUM(BoolGetDatum(false));
+}
+
+/*
+ * Given two BrinValues, update the first of them as a union of the summary
+ * values contained in both. The second one is untouched.
+ */
+Datum
+brin_minmax_multi_union(PG_FUNCTION_ARGS)
+{
+ BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+ BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
+ BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
+ // MinMaxOptions *opts = (MinMaxOptions *) PG_GET_OPCLASS_OPTIONS();
+ Oid colloid = PG_GET_COLLATION();
+ SerializedRanges *serialized_a;
+ SerializedRanges *serialized_b;
+ Ranges *ranges_a;
+ Ranges *ranges_b;
+ AttrNumber attno;
+ Form_pg_attribute attr;
+ CombineRange *cranges;
+ int ncranges;
+ FmgrInfo *cmpFn,
+ *distanceFn;
+ DistanceValue *distances;
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ Assert(col_a->bv_attno == col_b->bv_attno);
+ Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
+
+ attno = col_a->bv_attno;
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+
+ serialized_a = (SerializedRanges *) PG_DETOAST_DATUM(col_a->bv_values[0]);
+ serialized_b = (SerializedRanges *) PG_DETOAST_DATUM(col_b->bv_values[0]);
+
+ ranges_a = range_deserialize(serialized_a);
+ ranges_b = range_deserialize(serialized_b);
+
+ /* make sure neither of the ranges is NULL */
+ Assert(ranges_a && ranges_b);
+
+ ncranges = (ranges_a->nranges + ranges_a->nvalues) +
+ (ranges_b->nranges + ranges_b->nvalues);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* allocate and fill */
+ cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+
+ /* fill the combine ranges with entries for the first range */
+ fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
+ ranges_a);
+
+ /* and now add combine ranges for the second range */
+ fill_combine_ranges(&cranges[ranges_a->nranges + ranges_a->nvalues],
+ ranges_b->nranges + ranges_b->nvalues,
+ ranges_b);
+
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
+ BTLessStrategyNumber);
+
+ /* sort the combine ranges */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+
+ /*
+ * We've merged two different lists of ranges, so some of them may be
+ * overlapping. So walk through them and merge them.
+ */
+ ncranges = merge_combine_ranges(cmpFn, colloid, cranges, ncranges);
+
+ /* check that the combine ranges are correct (no overlaps, ordering) */
+ AssertValidCombineRanges(bdesc, colloid, attno, attr, cranges, ncranges);
+
+ /*
+ * 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, cranges, ncranges);
+
+ /*
+ * See how many values would be needed to store the current ranges,
+ * and if needed combine as many off them to get below the 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?
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ ranges_a->maxvalues,
+ cmpFn, colloid);
+
+ /* update the first range summary */
+ store_combine_ranges(ranges_a, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* cleanup and update the serialized value */
+ pfree(serialized_a);
+ col_a->bv_values[0] = PointerGetDatum(range_serialize(ranges_a));
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Cache and return minmax multi opclass support procedure
+ *
+ * Return the procedure corresponding to the given function support number
+ * or null if it is not exists.
+ */
+static FmgrInfo *
+minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
+{
+ MinmaxMultiOpaque *opaque;
+ uint16 basenum = procnum - PROCNUM_BASE;
+
+ /*
+ * We cache these in the opaque struct, to avoid repetitive syscache
+ * lookups.
+ */
+ opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+ /*
+ * If we already searched for this proc and didn't find it, don't bother
+ * searching again.
+ */
+ if (opaque->extra_proc_missing[basenum])
+ return NULL;
+
+ if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
+ {
+ if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
+ procnum)))
+ {
+ fmgr_info_copy(&opaque->extra_procinfos[basenum],
+ index_getprocinfo(bdesc->bd_index, attno, procnum),
+ bdesc->bd_context);
+ }
+ else
+ {
+ opaque->extra_proc_missing[basenum] = true;
+ return NULL;
+ }
+ }
+
+ return &opaque->extra_procinfos[basenum];
+}
+
+/*
+ * Cache and return the procedure for the given strategy.
+ *
+ * Note: this function mirrors minmax_multi_get_strategy_procinfo; see notes
+ * there. If changes are made here, see that function too.
+ */
+static FmgrInfo *
+minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
+ uint16 strategynum)
+{
+ MinmaxMultiOpaque *opaque;
+
+ Assert(strategynum >= 1 &&
+ strategynum <= BTMaxStrategyNumber);
+
+ opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+ /*
+ * We cache the procedures for the previous subtype in the opaque struct,
+ * to avoid repetitive syscache lookups. If the subtype changed,
+ * invalidate all the cached entries.
+ */
+ if (opaque->cached_subtype != subtype)
+ {
+ uint16 i;
+
+ for (i = 1; i <= BTMaxStrategyNumber; i++)
+ opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
+ opaque->cached_subtype = subtype;
+ }
+
+ if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
+ {
+ Form_pg_attribute attr;
+ HeapTuple tuple;
+ Oid opfamily,
+ oprid;
+ bool isNull;
+
+ opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
+ attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
+ tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(attr->atttypid),
+ ObjectIdGetDatum(subtype),
+ Int16GetDatum(strategynum));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
+ strategynum, attr->atttypid, subtype, opfamily);
+
+ oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple,
+ Anum_pg_amop_amopopr, &isNull));
+ ReleaseSysCache(tuple);
+ Assert(!isNull && RegProcedureIsValid(oprid));
+
+ fmgr_info_cxt(get_opcode(oprid),
+ &opaque->strategy_procinfos[strategynum - 1],
+ bdesc->bd_context);
+ }
+
+ return &opaque->strategy_procinfos[strategynum - 1];
+}
+
+Datum
+brin_minmax_multi_options(PG_FUNCTION_ARGS)
+{
+ local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+ init_local_reloptions(relopts, sizeof(MinMaxOptions));
+
+ add_local_int_reloption(relopts, "values_per_range", "desc",
+ 32, 8, 256, offsetof(MinMaxOptions, valuesPerRange));
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * brin_minmax_multi_summary_in
+ * - input routine for type brin_minmax_multi_summary.
+ *
+ * brin_minmax_multi_summary is only used internally to represent summaries
+ * in BRIN minmax-multi indexes, so it has no operations of its own, and we
+ * disallow input too.
+ */
+Datum
+brin_minmax_multi_summary_in(PG_FUNCTION_ARGS)
+{
+ /*
+ * brin_minmax_multi_summary stores the data in binary form and parsing
+ * text input is not needed, so disallow this.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "brin_minmax_multi_summary")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+
+/*
+ * brin_minmax_multi_summary_out
+ * - output routine for type brin_minmax_multi_summary.
+ *
+ * BRIN minmax-multi summaries are serialized into a bytea value, but we
+ * want to output something nicer humans can understand.
+ */
+Datum
+brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
+{
+ int i;
+ int idx;
+ SerializedRanges *ranges;
+ Ranges *ranges_deserialized;
+ StringInfoData str;
+ bool isvarlena;
+ Oid outfunc;
+ FmgrInfo fmgrinfo;
+ ArrayBuildState *astate_values = NULL;
+
+ initStringInfo(&str);
+ appendStringInfoChar(&str, '{');
+
+ /*
+ * 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);
+
+ 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 a7eb1c9473..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -159,6 +159,15 @@ 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
@@ -495,6 +504,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;
}
@@ -574,6 +588,10 @@ brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple)
dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno];
dtup->bt_columns[keyno].bv_allnulls = false;
+
+ dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL);
+ dtup->bt_columns[keyno].bv_serialize = NULL;
+ dtup->bt_columns[keyno].bv_context = dtup->bt_context;
}
MemoryContextSwitchTo(oldcxt);
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 0e52d75457..9cd5fa9f62 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -24,6 +24,7 @@ typedef struct BrinOptions
bool autosummarize;
double nDistinctPerRange; /* number of distinct values per range */
double falsePositiveRate; /* false positive for bloom filter */
+ int valuesPerRange; /* number of values per range */
} BrinOptions;
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 8cc4e532e6..fdaff42722 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -62,6 +62,9 @@ typedef struct BrinDesc
double bd_nDistinctPerRange;
double bd_falsePositiveRange;
+ /* parameters for multi-minmax indexes */
+ int bd_valuesPerRange;
+
/* per-column info; bd_tupdesc->natts entries long */
BrinOpcInfo *bd_info[FLEXIBLE_ARRAY_MEMBER];
} BrinDesc;
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 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..654584a03f 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -186,7 +186,7 @@ FullTransactionIdAdvance(FullTransactionId *dest)
* ----------
*/
#define FirstGenbkiObjectId 10000
-#define FirstBootstrapObjectId 12000
+#define FirstBootstrapObjectId 13000
#define FirstNormalObjectId 16384
/*
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 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 6709c8dfea..51403716b1 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_float8' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '1',
+ amproc => 'brin_minmax_multi_opcinfo' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '2',
+ amproc => 'brin_minmax_multi_add_value' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '3',
+ amproc => 'brin_minmax_multi_consistent' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '4',
+ amproc => 'brin_minmax_multi_union' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '5',
+ amproc => 'brin_minmax_multi_options' },
+{ amprocfamily => 'brin/datetime_minmax_multi_ops', amproclefttype => 'date',
+ amprocrighttype => 'timestamptz', amprocnum => '11',
+ amproc => 'brin_minmax_multi_distance_float8' },
+
# bloom datetime (date, timestamp, timestamptz)
{ amprocfamily => 'brin/datetime_bloom_ops', amproclefttype => 'timestamp',
amprocrighttype => 'timestamp', amprocnum => '1',
@@ -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 dea9adaf98..ba9231ac8c 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 => '9926',
+ opfmethod => 'brin', opfname => 'integer_minmax_multi_ops' },
{ oid => '9901',
opfmethod => 'brin', opfname => 'integer_bloom_ops' },
{ oid => '4055',
opfmethod => 'brin', opfname => 'numeric_minmax_ops' },
+{ oid => '9927',
+ opfmethod => 'brin', opfname => 'numeric_minmax_multi_ops' },
{ oid => '4056',
opfmethod => 'brin', opfname => 'text_minmax_ops' },
{ oid => '9902',
@@ -194,10 +198,14 @@
opfmethod => 'brin', opfname => 'numeric_bloom_ops' },
{ oid => '4058',
opfmethod => 'brin', opfname => 'timetz_minmax_ops' },
+{ oid => '9928',
+ opfmethod => 'brin', opfname => 'timetz_minmax_multi_ops' },
{ oid => '9904',
opfmethod => 'brin', opfname => 'timetz_bloom_ops' },
{ oid => '4059',
opfmethod => 'brin', opfname => 'datetime_minmax_ops' },
+{ oid => '9929',
+ opfmethod => 'brin', opfname => 'datetime_minmax_multi_ops' },
{ oid => '9905',
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 => '9930',
+ opfmethod => 'brin', opfname => 'oid_minmax_multi_ops' },
{ oid => '9909',
opfmethod => 'brin', opfname => 'oid_bloom_ops' },
{ oid => '4069',
opfmethod => 'brin', opfname => 'tid_minmax_ops' },
{ oid => '9910',
opfmethod => 'brin', opfname => 'tid_bloom_ops' },
+{ oid => '9931',
+ opfmethod => 'brin', opfname => 'tid_minmax_multi_ops' },
{ oid => '4070',
opfmethod => 'brin', opfname => 'float_minmax_ops' },
+{ oid => '9932',
+ opfmethod => 'brin', opfname => 'float_minmax_multi_ops' },
{ oid => '9911',
opfmethod => 'brin', opfname => 'float_bloom_ops' },
{ oid => '4074',
opfmethod => 'brin', opfname => 'macaddr_minmax_ops' },
+{ oid => '9933',
+ opfmethod => 'brin', opfname => 'macaddr_minmax_multi_ops' },
{ oid => '9912',
opfmethod => 'brin', opfname => 'macaddr_bloom_ops' },
{ oid => '4109',
opfmethod => 'brin', opfname => 'macaddr8_minmax_ops' },
+{ oid => '9934',
+ opfmethod => 'brin', opfname => 'macaddr8_minmax_multi_ops' },
{ oid => '9913',
opfmethod => 'brin', opfname => 'macaddr8_bloom_ops' },
{ oid => '4075',
opfmethod => 'brin', opfname => 'network_minmax_ops' },
+{ oid => '9935',
+ opfmethod => 'brin', opfname => 'network_minmax_multi_ops' },
{ oid => '4102',
opfmethod => 'brin', opfname => 'network_inclusion_ops' },
{ oid => '9914',
@@ -244,10 +264,14 @@
opfmethod => 'brin', opfname => 'bpchar_bloom_ops' },
{ oid => '4077',
opfmethod => 'brin', opfname => 'time_minmax_ops' },
+{ oid => '9936',
+ opfmethod => 'brin', opfname => 'time_minmax_multi_ops' },
{ oid => '9916',
opfmethod => 'brin', opfname => 'time_bloom_ops' },
{ oid => '4078',
opfmethod => 'brin', opfname => 'interval_minmax_ops' },
+{ oid => '9937',
+ opfmethod => 'brin', opfname => 'interval_minmax_multi_ops' },
{ oid => '9917',
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 => '9938',
+ opfmethod => 'brin', opfname => 'uuid_minmax_multi_ops' },
{ oid => '9918',
opfmethod => 'brin', opfname => 'uuid_bloom_ops' },
{ oid => '4103',
opfmethod => 'brin', opfname => 'range_inclusion_ops' },
{ oid => '4082',
opfmethod => 'brin', opfname => 'pg_lsn_minmax_ops' },
+{ oid => '9939',
+ opfmethod => 'brin', opfname => 'pg_lsn_minmax_multi_ops' },
{ oid => '9919',
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 3b92bb66e5..728763b97e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8197,6 +8197,77 @@
proname => 'brin_minmax_union', prorettype => 'bool',
proargtypes => 'internal internal internal', prosrc => 'brin_minmax_union' },
+# BRIN minmax multi
+{ oid => '9940', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_opcinfo', prorettype => 'internal',
+ proargtypes => 'internal', prosrc => 'brin_minmax_multi_opcinfo' },
+{ oid => '9941', 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 => '9942', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_consistent', prorettype => 'bool',
+ proargtypes => 'internal internal internal int4',
+ prosrc => 'brin_minmax_multi_consistent' },
+{ oid => '9943', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_union', prorettype => 'bool',
+ proargtypes => 'internal internal internal', prosrc => 'brin_minmax_multi_union' },
+{ oid => '9944', descr => 'BRIN multi minmax support',
+ proname => 'brin_minmax_multi_options', prorettype => 'void', proisstrict => 'f',
+ proargtypes => 'internal', prosrc => 'brin_minmax_multi_options' },
+
+{ oid => '9945', descr => 'BRIN multi minmax int2 distance',
+ proname => 'brin_minmax_multi_distance_int2', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int2' },
+{ oid => '9946', descr => 'BRIN multi minmax int4 distance',
+ proname => 'brin_minmax_multi_distance_int4', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int4' },
+{ oid => '9947', descr => 'BRIN multi minmax int8 distance',
+ proname => 'brin_minmax_multi_distance_int8', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_int8' },
+{ oid => '9948', descr => 'BRIN multi minmax float4 distance',
+ proname => 'brin_minmax_multi_distance_float4', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float4' },
+{ oid => '9949', descr => 'BRIN multi minmax float8 distance',
+ proname => 'brin_minmax_multi_distance_float8', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_float8' },
+{ oid => '9950', descr => 'BRIN multi minmax numeric distance',
+ proname => 'brin_minmax_multi_distance_numeric', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_numeric' },
+{ oid => '9951', descr => 'BRIN multi minmax tid distance',
+ proname => 'brin_minmax_multi_distance_tid', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_tid' },
+{ oid => '9952', descr => 'BRIN multi minmax uuid distance',
+ proname => 'brin_minmax_multi_distance_uuid', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_uuid' },
+{ oid => '9953', descr => 'BRIN multi minmax date distance',
+ proname => 'brin_minmax_multi_distance_date', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_date' },
+{ oid => '9954', descr => 'BRIN multi minmax time distance',
+ proname => 'brin_minmax_multi_distance_time', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_time' },
+{ oid => '9955', descr => 'BRIN multi minmax interval distance',
+ proname => 'brin_minmax_multi_distance_interval', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_interval' },
+{ oid => '9956', descr => 'BRIN multi minmax timetz distance',
+ proname => 'brin_minmax_multi_distance_timetz', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_timetz' },
+{ oid => '9957', 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 => '9958', descr => 'BRIN multi minmax macaddr distance',
+ proname => 'brin_minmax_multi_distance_macaddr', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr' },
+{ oid => '9959', descr => 'BRIN multi minmax macaddr8 distance',
+ proname => 'brin_minmax_multi_distance_macaddr8', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_macaddr8' },
+{ oid => '9960', descr => 'BRIN multi minmax inet distance',
+ proname => 'brin_minmax_multi_distance_inet', prorettype => 'float8',
+ proargtypes => 'internal internal', prosrc => 'brin_minmax_multi_distance_inet' },
+{ oid => '9961', 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',
@@ -11421,4 +11492,18 @@
proname => 'brin_bloom_summary_send', provolatile => 's', prorettype => 'bytea',
proargtypes => 'pg_brin_bloom_summary', prosrc => 'brin_bloom_summary_send' },
+{ oid => '9962', descr => 'I/O',
+ proname => 'brin_minmax_multi_summary_in', prorettype => 'pg_brin_minmax_multi_summary',
+ proargtypes => 'cstring', prosrc => 'brin_minmax_multi_summary_in' },
+{ oid => '9963', descr => 'I/O',
+ proname => 'brin_minmax_multi_summary_out', prorettype => 'cstring',
+ proargtypes => 'pg_brin_minmax_multi_summary', prosrc => 'brin_minmax_multi_summary_out' },
+{ oid => '9964', 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 => '9965', 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 74e279cbf9..e809094490 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 => '9966',
+ 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..e13cb59c7e
--- /dev/null
+++ b/src/test/regress/expected/brin_multi.out
@@ -0,0 +1,445 @@
+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 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 e568b9fea2..0541c12a25 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
5017 | pg_mcv_list
9925 | pg_brin_bloom_summary
-(5 rows)
+ 9966 | pg_brin_minmax_multi_summary
+(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 f1fed1037d..bfbbdbc894 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 9ed1468ad8..54d24cc184 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..6d61fb84c6
--- /dev/null
+++ b/src/test/regress/sql/brin_multi.sql
@@ -0,0 +1,397 @@
+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 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.26.2
--------------22A4B241170149838D4D1F8F
Content-Type: text/x-patch; charset=UTF-8;
name="0006-Batch-mode-when-building-new-BRIN-multi-min-20210215.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename*0="0006-Batch-mode-when-building-new-BRIN-multi-min-20210215.pa";
filename*1="tch"
^ permalink raw reply [nested|flat] 7+ messages in thread
* Re: WIP: new system catalog pg_wait_event
@ 2023-08-08 05:37 Drouvot, Bertrand <[email protected]>
2023-08-08 08:16 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
0 siblings, 1 reply; 7+ messages in thread
From: Drouvot, Bertrand @ 2023-08-08 05:37 UTC (permalink / raw)
To: Michael Paquier <[email protected]>; Kyotaro Horiguchi <[email protected]>; +Cc: [email protected]; [email protected]
Hi,
On 8/8/23 5:05 AM, Michael Paquier wrote:
> On Tue, Aug 08, 2023 at 11:53:32AM +0900, Kyotaro Horiguchi wrote:
>> As I mentioned in another thread, I'm uncertain about our stance on
>> the class id of the wait event. If a class acts as a namespace, we
>> should include it in the view. Otherwise, if the class id is just an
>> attribute of the wait event, we should make the event name unique.
>
> Including the class name in the view makes the most sense to me, FWIW,
> as it could be also possible that one reuses an event name in the
> existing in-core list, but for extensions. That's of course not
> something I would recommend.
Thanks Kyotaro-san and Michael for the feedback. I do agree and will
add the class name.
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 7+ messages in thread
* Re: WIP: new system catalog pg_wait_event
2023-08-08 05:37 Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
@ 2023-08-08 08:16 ` Drouvot, Bertrand <[email protected]>
2023-08-09 07:56 ` Re: WIP: new system catalog pg_wait_event Michael Paquier <[email protected]>
0 siblings, 1 reply; 7+ messages in thread
From: Drouvot, Bertrand @ 2023-08-08 08:16 UTC (permalink / raw)
To: Michael Paquier <[email protected]>; Kyotaro Horiguchi <[email protected]>; +Cc: [email protected]; [email protected]
Hi,
On 8/8/23 7:37 AM, Drouvot, Bertrand wrote:
> Hi,
>
> On 8/8/23 5:05 AM, Michael Paquier wrote:
>> On Tue, Aug 08, 2023 at 11:53:32AM +0900, Kyotaro Horiguchi wrote:
>>> As I mentioned in another thread, I'm uncertain about our stance on
>>> the class id of the wait event. If a class acts as a namespace, we
>>> should include it in the view. Otherwise, if the class id is just an
>>> attribute of the wait event, we should make the event name unique.
>>
>> Including the class name in the view makes the most sense to me, FWIW,
>> as it could be also possible that one reuses an event name in the
>> existing in-core list, but for extensions. That's of course not
>> something I would recommend.
>
> Thanks Kyotaro-san and Michael for the feedback. I do agree and will
> add the class name.
>
Please find attached v3 adding the wait event types.
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
From b54aa2dd33835a83bfe08a99338874200512fdf0 Mon Sep 17 00:00:00 2001
From: bdrouvotAWS <[email protected]>
Date: Sat, 5 Aug 2023 12:39:42 +0000
Subject: [PATCH v3] pg_wait_event
Adding a new system view, namely pg_wait_event, that describes the wait events.
---
doc/src/sgml/system-views.sgml | 64 +++++++++++++++
src/backend/catalog/system_views.sql | 3 +
src/backend/utils/activity/.gitignore | 1 +
src/backend/utils/activity/Makefile | 6 +-
.../activity/generate-wait_event_types.pl | 77 +++++++++++++------
src/backend/utils/activity/meson.build | 1 +
src/backend/utils/activity/pg_wait_event.c | 41 ++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/meson.build | 4 +-
src/test/regress/expected/rules.out | 4 +
src/test/regress/expected/sysviews.out | 11 +++
src/test/regress/sql/sysviews.sql | 7 ++
src/tools/msvc/clean.bat | 1 +
13 files changed, 200 insertions(+), 26 deletions(-)
21.6% doc/src/sgml/
56.7% src/backend/utils/activity/
5.2% src/include/catalog/
7.4% src/test/regress/expected/
4.0% src/test/regress/sql/
4.9% src/
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 57b228076e..ed26c8326f 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -221,6 +221,11 @@
<entry>views</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-wait-event"><structname>pg_wait_event</structname></link></entry>
+ <entry>wait events</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -4825,4 +4830,63 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+
+ <sect1 id="view-pg-wait-event">
+ <title><structname>pg_wait_event</structname></title>
+
+ <indexterm zone="view-pg-wait-event">
+ <primary>pg_wait_event</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_wait_event</structname> provides description about the
+ wait events.
+ </para>
+
+ <table>
+ <title><structname>pg_wait_event</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>wait_event_type</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event type
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>wait_event_name</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>description</structfield> <type>texte</type>
+ </para>
+ <para>
+ Wait event description
+ </para></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index af65af6bdd..f86a4dd770 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1342,3 +1342,6 @@ CREATE VIEW pg_stat_subscription_stats AS
ss.stats_reset
FROM pg_subscription as s,
pg_stat_get_subscription_stats(s.oid) as ss;
+
+CREATE VIEW pg_wait_event AS
+ SELECT * FROM pg_get_wait_events() AS we;
diff --git a/src/backend/utils/activity/.gitignore b/src/backend/utils/activity/.gitignore
index d77079285b..ad089a0b63 100644
--- a/src/backend/utils/activity/.gitignore
+++ b/src/backend/utils/activity/.gitignore
@@ -1,2 +1,3 @@
/pgstat_wait_event.c
/wait_event_types.h
+/pg_wait_event_insert.c
diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile
index f1117745d4..8595e6ea77 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -32,10 +32,14 @@ OBJS = \
pgstat_subscription.o \
pgstat_wal.o \
pgstat_xact.o \
+ pg_wait_event.o \
wait_event.o
include $(top_srcdir)/src/backend/common.mk
+pg_wait_event.o: pg_wait_event_insert.c
+pg_wait_event_insert.c: wait_event_types.h
+
wait_event.o: pgstat_wait_event.c
pgstat_wait_event.c: wait_event_types.h
touch $@
@@ -44,4 +48,4 @@ wait_event_types.h: $(top_srcdir)/src/backend/utils/activity/wait_event_names.tx
$(PERL) $(srcdir)/generate-wait_event_types.pl --code $<
maintainer-clean: clean
- rm -f wait_event_types.h pgstat_wait_event.c
+ rm -f wait_event_types.h pgstat_wait_event.c pg_wait_event_insert.c
diff --git a/src/backend/utils/activity/generate-wait_event_types.pl b/src/backend/utils/activity/generate-wait_event_types.pl
index 56335e8730..c3f58acb4b 100644
--- a/src/backend/utils/activity/generate-wait_event_types.pl
+++ b/src/backend/utils/activity/generate-wait_event_types.pl
@@ -4,6 +4,7 @@
# Generate wait events support files from wait_event_names.txt:
# - wait_event_types.h (if --code is passed)
# - pgstat_wait_event.c (if --code is passed)
+# - pg_wait_event_insert.c (if --code is passed)
# - wait_event_types.sgml (if --docs is passed)
#
# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
@@ -98,8 +99,10 @@ if ($gen_code)
# multiple times.
my $htmp = "$output_path/wait_event_types.h.tmp$$";
my $ctmp = "$output_path/pgstat_wait_event.c.tmp$$";
+ my $ictmp = "$output_path/pg_wait_event_insert.c.tmp$$";
open my $h, '>', $htmp or die "Could not open $htmp: $!";
open my $c, '>', $ctmp or die "Could not open $ctmp: $!";
+ open my $ic, '>', $ictmp or die "Could not open $ictmp: $!";
my $header_comment =
'/*-------------------------------------------------------------------------
@@ -129,16 +132,17 @@ if ($gen_code)
printf $c $header_comment, 'pgstat_wait_event.c';
+ printf $ic $header_comment, 'pg_wait_event_insert.c';
+
# uc() is being used to force the comparison to be case-insensitive.
foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe)
{
- # Don't generate .c and .h files for Extension, LWLock and
- # Lock, these are handled independently.
- next
- if ( $waitclass eq 'WaitEventExtension'
- || $waitclass eq 'WaitEventLWLock'
- || $waitclass eq 'WaitEventLock');
+ # Don't generate .c (except pg_wait_event_insert.c) and .h files for
+ # Extension, LWLock and Lock, these are handled independently.
+ my $is_exception = $waitclass eq 'WaitEventExtension' ||
+ $waitclass eq 'WaitEventLWLock' ||
+ $waitclass eq 'WaitEventLock';
my $last = $waitclass;
$last =~ s/^WaitEvent//;
@@ -147,50 +151,77 @@ if ($gen_code)
my $firstpass = 1;
my $pg_wait_class;
- printf $c
- "static const char *\npgstat_get_wait_$lastlc($waitclass w)\n{\n";
- printf $c "\tconst char *event_name = \"unknown wait event\";\n\n";
- printf $c "\tswitch (w)\n\t{\n";
+ if (!$is_exception)
+ {
+ printf $c
+ "static const char *\npgstat_get_wait_$lastlc($waitclass w)\n{\n";
+ printf $c "\tconst char *event_name = \"unknown wait event\";\n\n";
+ printf $c "\tswitch (w)\n\t{\n";
+ }
foreach my $wev (@{ $hashwe{$waitclass} })
{
- if ($firstpass)
+ if ($firstpass && !$is_exception)
{
printf $h "typedef enum\n{\n";
$pg_wait_class = "PG_WAIT_" . $lastuc;
printf $h "\t%s = %s", $wev->[0], $pg_wait_class;
$continue = ",\n";
}
- else
+ elsif (!$is_exception)
{
printf $h "%s\t%s", $continue, $wev->[0];
$continue = ",\n";
}
- $firstpass = 0;
- printf $c "\t\t case %s:\n", $wev->[0];
- # Apply quotes to the wait event name string.
- printf $c "\t\t\t event_name = \"%s\";\n\t\t\t break;\n",
- $wev->[1];
+ if (!$is_exception)
+ {
+ $firstpass = 0;
+
+ printf $c "\t\t case %s:\n", $wev->[0];
+ # Apply quotes to the wait event name string.
+ printf $c "\t\t\t event_name = \"%s\";\n\t\t\t break;\n",
+ $wev->[1];
+ }
+
+ my $new_desc = substr $wev->[2], 1, -2;
+ $new_desc =~ s/'/\\'/g;
+ $new_desc =~ s/<.*>(.*?)<.*>/$1/g;
+ $new_desc =~ s/<xref linkend="guc-(.*?)"\/>/$1/g;
+ $new_desc =~ s/; see.*$//;
+
+ printf $ic "\tmemset(values, 0, sizeof(values));\n";
+ printf $ic "\tmemset(nulls, 0, sizeof(nulls));\n\n";
+ printf $ic "\tvalues[0] = CStringGetTextDatum(\"%s\");\n", $last;
+ printf $ic "\tvalues[1] = CStringGetTextDatum(\"%s\");\n", $wev->[1];
+ printf $ic "\tvalues[2] = CStringGetTextDatum(\"%s\");\n\n", $new_desc;
+
+ printf $ic "\ttuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);\n\n";
}
- printf $h "\n} $waitclass;\n\n";
+ if (!$is_exception)
+ {
+ printf $h "\n} $waitclass;\n\n";
- printf $c
- "\t\t\t /* no default case, so that compiler will warn */\n";
- printf $c "\t}\n\n";
- printf $c "\treturn event_name;\n";
- printf $c "}\n\n";
+ printf $c
+ "\t\t\t /* no default case, so that compiler will warn */\n";
+ printf $c "\t}\n\n";
+ printf $c "\treturn event_name;\n";
+ printf $c "}\n\n";
+ }
}
printf $h "#endif /* WAIT_EVENT_TYPES_H */\n";
close $h;
close $c;
+ close $ic;
rename($htmp, "$output_path/wait_event_types.h")
|| die "rename: $htmp to $output_path/wait_event_types.h: $!";
rename($ctmp, "$output_path/pgstat_wait_event.c")
|| die "rename: $ctmp to $output_path/pgstat_wait_event.c: $!";
+ rename($ictmp, "$output_path/pg_wait_event_insert.c")
+ || die "rename: $ictmp to $output_path/pg_wait_event_insert.c: $!";
}
# Generate the .sgml file.
elsif ($gen_docs)
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 9633f3623c..774e0bd348 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -23,6 +23,7 @@ backend_sources += files(
# seems nicer to not add that as an include path for the whole backend.
waitevent_sources = files(
'wait_event.c',
+ 'pg_wait_event.c',
)
wait_event = static_library('wait_event_names',
diff --git a/src/backend/utils/activity/pg_wait_event.c b/src/backend/utils/activity/pg_wait_event.c
new file mode 100644
index 0000000000..e6bfe36d4d
--- /dev/null
+++ b/src/backend/utils/activity/pg_wait_event.c
@@ -0,0 +1,41 @@
+/* ----------
+ * pg_wait_event.c
+ * Wait event reporting infrastructure.
+ *
+ * Copyright (c) 2001-2023, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/activity/pg_wait_event.c
+ *
+ * NOTES
+ *
+ * The "pg_wait_event_insert.c" included in this file is generated by
+ * src/backend/utils/activity/generate-wait_event_types.pl
+ *
+ */
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+
+/*
+ * This function lists the wait events and their descriptions.
+ *
+ * The system view pg_wait_event provides a user interface to this
+ * SRF.
+ */
+Datum
+pg_get_wait_events(PG_FUNCTION_ARGS)
+{
+#define NUM_WAIT_EVENT_TABLES_ELEM 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ Datum values[NUM_WAIT_EVENT_TABLES_ELEM];
+ bool nulls[NUM_WAIT_EVENT_TABLES_ELEM];
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ #include "pg_wait_event_insert.c"
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6996073989..6d10d2e5be 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5417,6 +5417,12 @@
proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_delegation,leader_pid,query_id}',
prosrc => 'pg_stat_get_activity' },
+{ oid => '8403', descr => 'describe wait events',
+ proname => 'pg_get_wait_events', procost => '10', prorows => '100',
+ proretset => 't', provolatile => 's', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,text}',
+ proargmodes => '{o,o,o}', proargnames => '{wait_event_type,wait_event_name,description}',
+ prosrc => 'pg_get_wait_events' },
{ oid => '3318',
descr => 'statistics: information about progress of backends running maintenance command',
proname => 'pg_stat_get_progress_info', prorows => '100', proretset => 't',
diff --git a/src/include/utils/meson.build b/src/include/utils/meson.build
index 6de5d93799..4f14f770ef 100644
--- a/src/include/utils/meson.build
+++ b/src/include/utils/meson.build
@@ -1,6 +1,6 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c']
+wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c', 'pg_wait_event_insert.c']
wait_event_target = custom_target('wait_event_names',
input: files('../../backend/utils/activity/wait_event_names.txt'),
output: wait_event_output,
@@ -11,7 +11,7 @@ wait_event_target = custom_target('wait_event_names',
],
build_by_default: true,
install: true,
- install_dir: [dir_include_server / 'utils', false],
+ install_dir: [dir_include_server / 'utils', false, false],
)
wait_event_types_h = wait_event_target[0]
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e07afcd4aa..f160cb5642 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2631,6 +2631,10 @@ pg_views| SELECT n.nspname AS schemaname,
FROM (pg_class c
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE (c.relkind = 'v'::"char");
+pg_wait_event| SELECT wait_event_type,
+ wait_event_name,
+ description
+ FROM pg_get_wait_events() we(wait_event_type, wait_event_name, description);
SELECT tablename, rulename, definition FROM pg_rules
WHERE schemaname = 'pg_catalog'
ORDER BY tablename, rulename;
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 001c6e7eb9..bcf251c0c8 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -134,6 +134,17 @@ select name, setting from pg_settings where name like 'enable%';
enable_tidscan | on
(21 rows)
+-- There will surely be at least 9 wait event types, 240 wait events and at
+-- least 27 related to WAL
+select count(distinct(wait_event_type)) > 8 as ok_type,
+ count(*) > 239 as ok,
+ count(*) FILTER (WHERE description like '%WAL%') > 26 AS ok_wal_desc
+from pg_wait_event;
+ ok_type | ok | ok_wal_desc
+---------+----+-------------
+ t | t | t
+(1 row)
+
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail
-- without the outputs changing anytime IANA updates the underlying data,
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 351e469c77..7a9dc6021d 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -55,6 +55,13 @@ select count(*) = 0 as ok from pg_stat_wal_receiver;
-- a regression test run.
select name, setting from pg_settings where name like 'enable%';
+-- There will surely be at least 9 wait event types, 240 wait events and at
+-- least 27 related to WAL
+select count(distinct(wait_event_type)) > 8 as ok_type,
+ count(*) > 239 as ok,
+ count(*) FILTER (WHERE description like '%WAL%') > 26 AS ok_wal_desc
+from pg_wait_event;
+
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail
-- without the outputs changing anytime IANA updates the underlying data,
diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat
index 7cb23ea894..028d174d87 100755
--- a/src/tools/msvc/clean.bat
+++ b/src/tools/msvc/clean.bat
@@ -55,6 +55,7 @@ if exist src\include\catalog\header-stamp del /q src\include\catalog\header-stam
if exist doc\src\sgml\version.sgml del /q doc\src\sgml\version.sgml
if %DIST%==1 if exist src\backend\utils\activity\pgstat_wait_event.c del /q src\backend\utils\activity\pgstat_wait_event.c
+if %DIST%==1 if exist src\backend\utils\activity\pg_wait_event_insert.c del /q src\backend\utils\activity\pg_wait_event_insert.c
if %DIST%==1 if exist src\backend\utils\activity\wait_event_types.h del /q src\backend\utils\activity\wait_event_types.h
if %DIST%==1 if exist src\backend\utils\fmgroids.h del /q src\backend\utils\fmgroids.h
if %DIST%==1 if exist src\backend\utils\fmgrprotos.h del /q src\backend\utils\fmgrprotos.h
--
2.34.1
Attachments:
[text/plain] v3-0001-pg_wait_event.patch (16.7K, ../../[email protected]/2-v3-0001-pg_wait_event.patch)
download | inline diff:
From b54aa2dd33835a83bfe08a99338874200512fdf0 Mon Sep 17 00:00:00 2001
From: bdrouvotAWS <[email protected]>
Date: Sat, 5 Aug 2023 12:39:42 +0000
Subject: [PATCH v3] pg_wait_event
Adding a new system view, namely pg_wait_event, that describes the wait events.
---
doc/src/sgml/system-views.sgml | 64 +++++++++++++++
src/backend/catalog/system_views.sql | 3 +
src/backend/utils/activity/.gitignore | 1 +
src/backend/utils/activity/Makefile | 6 +-
.../activity/generate-wait_event_types.pl | 77 +++++++++++++------
src/backend/utils/activity/meson.build | 1 +
src/backend/utils/activity/pg_wait_event.c | 41 ++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/meson.build | 4 +-
src/test/regress/expected/rules.out | 4 +
src/test/regress/expected/sysviews.out | 11 +++
src/test/regress/sql/sysviews.sql | 7 ++
src/tools/msvc/clean.bat | 1 +
13 files changed, 200 insertions(+), 26 deletions(-)
21.6% doc/src/sgml/
56.7% src/backend/utils/activity/
5.2% src/include/catalog/
7.4% src/test/regress/expected/
4.0% src/test/regress/sql/
4.9% src/
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 57b228076e..ed26c8326f 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -221,6 +221,11 @@
<entry>views</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-wait-event"><structname>pg_wait_event</structname></link></entry>
+ <entry>wait events</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -4825,4 +4830,63 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+
+ <sect1 id="view-pg-wait-event">
+ <title><structname>pg_wait_event</structname></title>
+
+ <indexterm zone="view-pg-wait-event">
+ <primary>pg_wait_event</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_wait_event</structname> provides description about the
+ wait events.
+ </para>
+
+ <table>
+ <title><structname>pg_wait_event</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>wait_event_type</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event type
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>wait_event_name</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>description</structfield> <type>texte</type>
+ </para>
+ <para>
+ Wait event description
+ </para></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index af65af6bdd..f86a4dd770 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1342,3 +1342,6 @@ CREATE VIEW pg_stat_subscription_stats AS
ss.stats_reset
FROM pg_subscription as s,
pg_stat_get_subscription_stats(s.oid) as ss;
+
+CREATE VIEW pg_wait_event AS
+ SELECT * FROM pg_get_wait_events() AS we;
diff --git a/src/backend/utils/activity/.gitignore b/src/backend/utils/activity/.gitignore
index d77079285b..ad089a0b63 100644
--- a/src/backend/utils/activity/.gitignore
+++ b/src/backend/utils/activity/.gitignore
@@ -1,2 +1,3 @@
/pgstat_wait_event.c
/wait_event_types.h
+/pg_wait_event_insert.c
diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile
index f1117745d4..8595e6ea77 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -32,10 +32,14 @@ OBJS = \
pgstat_subscription.o \
pgstat_wal.o \
pgstat_xact.o \
+ pg_wait_event.o \
wait_event.o
include $(top_srcdir)/src/backend/common.mk
+pg_wait_event.o: pg_wait_event_insert.c
+pg_wait_event_insert.c: wait_event_types.h
+
wait_event.o: pgstat_wait_event.c
pgstat_wait_event.c: wait_event_types.h
touch $@
@@ -44,4 +48,4 @@ wait_event_types.h: $(top_srcdir)/src/backend/utils/activity/wait_event_names.tx
$(PERL) $(srcdir)/generate-wait_event_types.pl --code $<
maintainer-clean: clean
- rm -f wait_event_types.h pgstat_wait_event.c
+ rm -f wait_event_types.h pgstat_wait_event.c pg_wait_event_insert.c
diff --git a/src/backend/utils/activity/generate-wait_event_types.pl b/src/backend/utils/activity/generate-wait_event_types.pl
index 56335e8730..c3f58acb4b 100644
--- a/src/backend/utils/activity/generate-wait_event_types.pl
+++ b/src/backend/utils/activity/generate-wait_event_types.pl
@@ -4,6 +4,7 @@
# Generate wait events support files from wait_event_names.txt:
# - wait_event_types.h (if --code is passed)
# - pgstat_wait_event.c (if --code is passed)
+# - pg_wait_event_insert.c (if --code is passed)
# - wait_event_types.sgml (if --docs is passed)
#
# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
@@ -98,8 +99,10 @@ if ($gen_code)
# multiple times.
my $htmp = "$output_path/wait_event_types.h.tmp$$";
my $ctmp = "$output_path/pgstat_wait_event.c.tmp$$";
+ my $ictmp = "$output_path/pg_wait_event_insert.c.tmp$$";
open my $h, '>', $htmp or die "Could not open $htmp: $!";
open my $c, '>', $ctmp or die "Could not open $ctmp: $!";
+ open my $ic, '>', $ictmp or die "Could not open $ictmp: $!";
my $header_comment =
'/*-------------------------------------------------------------------------
@@ -129,16 +132,17 @@ if ($gen_code)
printf $c $header_comment, 'pgstat_wait_event.c';
+ printf $ic $header_comment, 'pg_wait_event_insert.c';
+
# uc() is being used to force the comparison to be case-insensitive.
foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe)
{
- # Don't generate .c and .h files for Extension, LWLock and
- # Lock, these are handled independently.
- next
- if ( $waitclass eq 'WaitEventExtension'
- || $waitclass eq 'WaitEventLWLock'
- || $waitclass eq 'WaitEventLock');
+ # Don't generate .c (except pg_wait_event_insert.c) and .h files for
+ # Extension, LWLock and Lock, these are handled independently.
+ my $is_exception = $waitclass eq 'WaitEventExtension' ||
+ $waitclass eq 'WaitEventLWLock' ||
+ $waitclass eq 'WaitEventLock';
my $last = $waitclass;
$last =~ s/^WaitEvent//;
@@ -147,50 +151,77 @@ if ($gen_code)
my $firstpass = 1;
my $pg_wait_class;
- printf $c
- "static const char *\npgstat_get_wait_$lastlc($waitclass w)\n{\n";
- printf $c "\tconst char *event_name = \"unknown wait event\";\n\n";
- printf $c "\tswitch (w)\n\t{\n";
+ if (!$is_exception)
+ {
+ printf $c
+ "static const char *\npgstat_get_wait_$lastlc($waitclass w)\n{\n";
+ printf $c "\tconst char *event_name = \"unknown wait event\";\n\n";
+ printf $c "\tswitch (w)\n\t{\n";
+ }
foreach my $wev (@{ $hashwe{$waitclass} })
{
- if ($firstpass)
+ if ($firstpass && !$is_exception)
{
printf $h "typedef enum\n{\n";
$pg_wait_class = "PG_WAIT_" . $lastuc;
printf $h "\t%s = %s", $wev->[0], $pg_wait_class;
$continue = ",\n";
}
- else
+ elsif (!$is_exception)
{
printf $h "%s\t%s", $continue, $wev->[0];
$continue = ",\n";
}
- $firstpass = 0;
- printf $c "\t\t case %s:\n", $wev->[0];
- # Apply quotes to the wait event name string.
- printf $c "\t\t\t event_name = \"%s\";\n\t\t\t break;\n",
- $wev->[1];
+ if (!$is_exception)
+ {
+ $firstpass = 0;
+
+ printf $c "\t\t case %s:\n", $wev->[0];
+ # Apply quotes to the wait event name string.
+ printf $c "\t\t\t event_name = \"%s\";\n\t\t\t break;\n",
+ $wev->[1];
+ }
+
+ my $new_desc = substr $wev->[2], 1, -2;
+ $new_desc =~ s/'/\\'/g;
+ $new_desc =~ s/<.*>(.*?)<.*>/$1/g;
+ $new_desc =~ s/<xref linkend="guc-(.*?)"\/>/$1/g;
+ $new_desc =~ s/; see.*$//;
+
+ printf $ic "\tmemset(values, 0, sizeof(values));\n";
+ printf $ic "\tmemset(nulls, 0, sizeof(nulls));\n\n";
+ printf $ic "\tvalues[0] = CStringGetTextDatum(\"%s\");\n", $last;
+ printf $ic "\tvalues[1] = CStringGetTextDatum(\"%s\");\n", $wev->[1];
+ printf $ic "\tvalues[2] = CStringGetTextDatum(\"%s\");\n\n", $new_desc;
+
+ printf $ic "\ttuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);\n\n";
}
- printf $h "\n} $waitclass;\n\n";
+ if (!$is_exception)
+ {
+ printf $h "\n} $waitclass;\n\n";
- printf $c
- "\t\t\t /* no default case, so that compiler will warn */\n";
- printf $c "\t}\n\n";
- printf $c "\treturn event_name;\n";
- printf $c "}\n\n";
+ printf $c
+ "\t\t\t /* no default case, so that compiler will warn */\n";
+ printf $c "\t}\n\n";
+ printf $c "\treturn event_name;\n";
+ printf $c "}\n\n";
+ }
}
printf $h "#endif /* WAIT_EVENT_TYPES_H */\n";
close $h;
close $c;
+ close $ic;
rename($htmp, "$output_path/wait_event_types.h")
|| die "rename: $htmp to $output_path/wait_event_types.h: $!";
rename($ctmp, "$output_path/pgstat_wait_event.c")
|| die "rename: $ctmp to $output_path/pgstat_wait_event.c: $!";
+ rename($ictmp, "$output_path/pg_wait_event_insert.c")
+ || die "rename: $ictmp to $output_path/pg_wait_event_insert.c: $!";
}
# Generate the .sgml file.
elsif ($gen_docs)
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 9633f3623c..774e0bd348 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -23,6 +23,7 @@ backend_sources += files(
# seems nicer to not add that as an include path for the whole backend.
waitevent_sources = files(
'wait_event.c',
+ 'pg_wait_event.c',
)
wait_event = static_library('wait_event_names',
diff --git a/src/backend/utils/activity/pg_wait_event.c b/src/backend/utils/activity/pg_wait_event.c
new file mode 100644
index 0000000000..e6bfe36d4d
--- /dev/null
+++ b/src/backend/utils/activity/pg_wait_event.c
@@ -0,0 +1,41 @@
+/* ----------
+ * pg_wait_event.c
+ * Wait event reporting infrastructure.
+ *
+ * Copyright (c) 2001-2023, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/activity/pg_wait_event.c
+ *
+ * NOTES
+ *
+ * The "pg_wait_event_insert.c" included in this file is generated by
+ * src/backend/utils/activity/generate-wait_event_types.pl
+ *
+ */
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+
+/*
+ * This function lists the wait events and their descriptions.
+ *
+ * The system view pg_wait_event provides a user interface to this
+ * SRF.
+ */
+Datum
+pg_get_wait_events(PG_FUNCTION_ARGS)
+{
+#define NUM_WAIT_EVENT_TABLES_ELEM 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ Datum values[NUM_WAIT_EVENT_TABLES_ELEM];
+ bool nulls[NUM_WAIT_EVENT_TABLES_ELEM];
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ #include "pg_wait_event_insert.c"
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6996073989..6d10d2e5be 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5417,6 +5417,12 @@
proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_delegation,leader_pid,query_id}',
prosrc => 'pg_stat_get_activity' },
+{ oid => '8403', descr => 'describe wait events',
+ proname => 'pg_get_wait_events', procost => '10', prorows => '100',
+ proretset => 't', provolatile => 's', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,text}',
+ proargmodes => '{o,o,o}', proargnames => '{wait_event_type,wait_event_name,description}',
+ prosrc => 'pg_get_wait_events' },
{ oid => '3318',
descr => 'statistics: information about progress of backends running maintenance command',
proname => 'pg_stat_get_progress_info', prorows => '100', proretset => 't',
diff --git a/src/include/utils/meson.build b/src/include/utils/meson.build
index 6de5d93799..4f14f770ef 100644
--- a/src/include/utils/meson.build
+++ b/src/include/utils/meson.build
@@ -1,6 +1,6 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c']
+wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c', 'pg_wait_event_insert.c']
wait_event_target = custom_target('wait_event_names',
input: files('../../backend/utils/activity/wait_event_names.txt'),
output: wait_event_output,
@@ -11,7 +11,7 @@ wait_event_target = custom_target('wait_event_names',
],
build_by_default: true,
install: true,
- install_dir: [dir_include_server / 'utils', false],
+ install_dir: [dir_include_server / 'utils', false, false],
)
wait_event_types_h = wait_event_target[0]
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e07afcd4aa..f160cb5642 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2631,6 +2631,10 @@ pg_views| SELECT n.nspname AS schemaname,
FROM (pg_class c
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE (c.relkind = 'v'::"char");
+pg_wait_event| SELECT wait_event_type,
+ wait_event_name,
+ description
+ FROM pg_get_wait_events() we(wait_event_type, wait_event_name, description);
SELECT tablename, rulename, definition FROM pg_rules
WHERE schemaname = 'pg_catalog'
ORDER BY tablename, rulename;
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 001c6e7eb9..bcf251c0c8 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -134,6 +134,17 @@ select name, setting from pg_settings where name like 'enable%';
enable_tidscan | on
(21 rows)
+-- There will surely be at least 9 wait event types, 240 wait events and at
+-- least 27 related to WAL
+select count(distinct(wait_event_type)) > 8 as ok_type,
+ count(*) > 239 as ok,
+ count(*) FILTER (WHERE description like '%WAL%') > 26 AS ok_wal_desc
+from pg_wait_event;
+ ok_type | ok | ok_wal_desc
+---------+----+-------------
+ t | t | t
+(1 row)
+
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail
-- without the outputs changing anytime IANA updates the underlying data,
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 351e469c77..7a9dc6021d 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -55,6 +55,13 @@ select count(*) = 0 as ok from pg_stat_wal_receiver;
-- a regression test run.
select name, setting from pg_settings where name like 'enable%';
+-- There will surely be at least 9 wait event types, 240 wait events and at
+-- least 27 related to WAL
+select count(distinct(wait_event_type)) > 8 as ok_type,
+ count(*) > 239 as ok,
+ count(*) FILTER (WHERE description like '%WAL%') > 26 AS ok_wal_desc
+from pg_wait_event;
+
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail
-- without the outputs changing anytime IANA updates the underlying data,
diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat
index 7cb23ea894..028d174d87 100755
--- a/src/tools/msvc/clean.bat
+++ b/src/tools/msvc/clean.bat
@@ -55,6 +55,7 @@ if exist src\include\catalog\header-stamp del /q src\include\catalog\header-stam
if exist doc\src\sgml\version.sgml del /q doc\src\sgml\version.sgml
if %DIST%==1 if exist src\backend\utils\activity\pgstat_wait_event.c del /q src\backend\utils\activity\pgstat_wait_event.c
+if %DIST%==1 if exist src\backend\utils\activity\pg_wait_event_insert.c del /q src\backend\utils\activity\pg_wait_event_insert.c
if %DIST%==1 if exist src\backend\utils\activity\wait_event_types.h del /q src\backend\utils\activity\wait_event_types.h
if %DIST%==1 if exist src\backend\utils\fmgroids.h del /q src\backend\utils\fmgroids.h
if %DIST%==1 if exist src\backend\utils\fmgrprotos.h del /q src\backend\utils\fmgrprotos.h
--
2.34.1
^ permalink raw reply [nested|flat] 7+ messages in thread
* Re: WIP: new system catalog pg_wait_event
2023-08-08 05:37 Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-08 08:16 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
@ 2023-08-09 07:56 ` Michael Paquier <[email protected]>
2023-08-10 18:09 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
0 siblings, 1 reply; 7+ messages in thread
From: Michael Paquier @ 2023-08-09 07:56 UTC (permalink / raw)
To: Drouvot, Bertrand <[email protected]>; +Cc: Kyotaro Horiguchi <[email protected]>; [email protected]; [email protected]
On Tue, Aug 08, 2023 at 10:16:37AM +0200, Drouvot, Bertrand wrote:
> Please find attached v3 adding the wait event types.
+-- There will surely be at least 9 wait event types, 240 wait events and at
+-- least 27 related to WAL
+select count(distinct(wait_event_type)) > 8 as ok_type,
+ count(*) > 239 as ok,
+ count(*) FILTER (WHERE description like '%WAL%') > 26 AS ok_wal_desc
+from pg_wait_event;
The point is to check the execution of this function, so this could be
simpler, like that or a GROUP BY clause with the event type:
SELECT count(*) > 0 FROM pg_wait_event;
SELECT wait_event_type, count(*) > 0 AS has_data FROM pg_wait_event
GROUP BY wait_event_type ORDER BY wait_event_type;
+ printf $ic "\tmemset(values, 0, sizeof(values));\n";
+ printf $ic "\tmemset(nulls, 0, sizeof(nulls));\n\n";
+ printf $ic "\tvalues[0] = CStringGetTextDatum(\"%s\");\n", $last;
+ printf $ic "\tvalues[1] = CStringGetTextDatum(\"%s\");\n", $wev->[1];
+ printf $ic "\tvalues[2] = CStringGetTextDatum(\"%s\");\n\n", $new_desc;
That's overcomplicated for some code generated. Wouldn't it be
simpler to generate a list of elements, with the code inserting the
tuples materialized looping over it?
+ my $new_desc = substr $wev->[2], 1, -2;
+ $new_desc =~ s/'/\\'/g;
+ $new_desc =~ s/<.*>(.*?)<.*>/$1/g;
+ $new_desc =~ s/<xref linkend="guc-(.*?)"\/>/$1/g;
+ $new_desc =~ s/; see.*$//;
Better to document what this does, the contents produced look good.
+ rename($ictmp, "$output_path/pg_wait_event_insert.c")
+ || die "rename: $ictmp to $output_path/pg_wait_event_insert.c: $!";
# seems nicer to not add that as an include path for the whole backend.
waitevent_sources = files(
'wait_event.c',
+ 'pg_wait_event.c',
)
This could use a name referring to SQL functions, say
wait_event_funcs.c, with a wait_event_data.c or a
wait_event_funcs_data.c?
+ # Don't generate .c (except pg_wait_event_insert.c) and .h files for
+ # Extension, LWLock and Lock, these are handled independently.
+ my $is_exception = $waitclass eq 'WaitEventExtension' ||
+ $waitclass eq 'WaitEventLWLock' ||
+ $waitclass eq 'WaitEventLock';
Perhaps it would be cleaner to use a separate loop?
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, ../../[email protected]/2-signature.asc)
download
^ permalink raw reply [nested|flat] 7+ messages in thread
* Re: WIP: new system catalog pg_wait_event
2023-08-08 05:37 Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-08 08:16 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-09 07:56 ` Re: WIP: new system catalog pg_wait_event Michael Paquier <[email protected]>
@ 2023-08-10 18:09 ` Drouvot, Bertrand <[email protected]>
2023-08-14 04:37 ` Re: WIP: new system catalog pg_wait_event Michael Paquier <[email protected]>
0 siblings, 1 reply; 7+ messages in thread
From: Drouvot, Bertrand @ 2023-08-10 18:09 UTC (permalink / raw)
To: Michael Paquier <[email protected]>; +Cc: Kyotaro Horiguchi <[email protected]>; [email protected]; [email protected]
Hi,
On 8/9/23 9:56 AM, Michael Paquier wrote:
> On Tue, Aug 08, 2023 at 10:16:37AM +0200, Drouvot, Bertrand wrote:
>> Please find attached v3 adding the wait event types.
>
> +-- There will surely be at least 9 wait event types, 240 wait events and at
> +-- least 27 related to WAL
> +select count(distinct(wait_event_type)) > 8 as ok_type,
> + count(*) > 239 as ok,
> + count(*) FILTER (WHERE description like '%WAL%') > 26 AS ok_wal_desc
> +from pg_wait_event;
> The point is to check the execution of this function, so this could be
> simpler, like that or a GROUP BY clause with the event type:
> SELECT count(*) > 0 FROM pg_wait_event;
> SELECT wait_event_type, count(*) > 0 AS has_data FROM pg_wait_event
> GROUP BY wait_event_type ORDER BY wait_event_type;
>
Thanks for looking at it!
Right, so v4 attached is just testing "SELECT count(*) > 0 FROM pg_wait_event;",
that does look enough to test.
> + printf $ic "\tmemset(values, 0, sizeof(values));\n";
> + printf $ic "\tmemset(nulls, 0, sizeof(nulls));\n\n";
> + printf $ic "\tvalues[0] = CStringGetTextDatum(\"%s\");\n", $last;
> + printf $ic "\tvalues[1] = CStringGetTextDatum(\"%s\");\n", $wev->[1];
> + printf $ic "\tvalues[2] = CStringGetTextDatum(\"%s\");\n\n", $new_desc;
>
> That's overcomplicated for some code generated. Wouldn't it be
> simpler to generate a list of elements, with the code inserting the
> tuples materialized looping over it?
>
Yeah, agree thanks!
In v4, the perl script now appends the wait events in a List that way:
"
printf $ic "\telement = (wait_event_element *) palloc(sizeof(wait_event_element));\n";
printf $ic "\telement->wait_event_type = \"%s\";\n", $last;
printf $ic "\telement->wait_event_name = \"%s\";\n", $wev->[1];
printf $ic "\telement->wait_event_description = \"%s\";\n\n", $new_desc;
printf $ic "\twait_event = lappend(wait_event, element);\n\n";
"
And the C function pg_get_wait_events() now iterates over this List.
> + my $new_desc = substr $wev->[2], 1, -2;
> + $new_desc =~ s/'/\\'/g;
> + $new_desc =~ s/<.*>(.*?)<.*>/$1/g;
> + $new_desc =~ s/<xref linkend="guc-(.*?)"\/>/$1/g;
> + $new_desc =~ s/; see.*$//;
> Better to document what this does,
good idea...
I had to turn them "on" one by one to recall why they are there...;-)
Done in v4.
> the contents produced look good.
yeap
>
> + rename($ictmp, "$output_path/pg_wait_event_insert.c")
> + || die "rename: $ictmp to $output_path/pg_wait_event_insert.c: $!";
>
> # seems nicer to not add that as an include path for the whole backend.
> waitevent_sources = files(
> 'wait_event.c',
> + 'pg_wait_event.c',
> )
>
> This could use a name referring to SQL functions, say
> wait_event_funcs.c, with a wait_event_data.c or a
> wait_event_funcs_data.c?
That sounds better indeed, thanks! v4 is using wait_event_funcs.c and
wait_event_funcs_data.c.
>
> + # Don't generate .c (except pg_wait_event_insert.c) and .h files for
> + # Extension, LWLock and Lock, these are handled independently.
> + my $is_exception = $waitclass eq 'WaitEventExtension' ||
> + $waitclass eq 'WaitEventLWLock' ||
> + $waitclass eq 'WaitEventLock';
> Perhaps it would be cleaner to use a separate loop?
Agree that's worth it given the fact that iterating one more time is not that
costly here.
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
From aa3e81810daaba7b1b2e05219ef662f2fd0607f8 Mon Sep 17 00:00:00 2001
From: bdrouvotAWS <[email protected]>
Date: Sat, 5 Aug 2023 12:39:42 +0000
Subject: [PATCH v4] pg_wait_event
Adding a new system view, namely pg_wait_event, that describes the wait events.
---
doc/src/sgml/system-views.sgml | 64 +++++++++++++++++
src/backend/catalog/system_views.sql | 3 +
src/backend/utils/activity/.gitignore | 1 +
src/backend/utils/activity/Makefile | 6 +-
.../activity/generate-wait_event_types.pl | 46 ++++++++++++-
src/backend/utils/activity/meson.build | 1 +
src/backend/utils/activity/wait_event_funcs.c | 69 +++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/meson.build | 4 +-
src/test/regress/expected/rules.out | 4 ++
src/test/regress/expected/sysviews.out | 7 ++
src/test/regress/sql/sysviews.sql | 3 +
src/tools/msvc/clean.bat | 1 +
13 files changed, 209 insertions(+), 6 deletions(-)
24.4% doc/src/sgml/
58.5% src/backend/utils/activity/
5.9% src/include/catalog/
4.1% src/test/regress/expected/
6.9% src/
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 57b228076e..ed26c8326f 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -221,6 +221,11 @@
<entry>views</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-wait-event"><structname>pg_wait_event</structname></link></entry>
+ <entry>wait events</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -4825,4 +4830,63 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+
+ <sect1 id="view-pg-wait-event">
+ <title><structname>pg_wait_event</structname></title>
+
+ <indexterm zone="view-pg-wait-event">
+ <primary>pg_wait_event</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_wait_event</structname> provides description about the
+ wait events.
+ </para>
+
+ <table>
+ <title><structname>pg_wait_event</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>wait_event_type</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event type
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>wait_event_name</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>description</structfield> <type>texte</type>
+ </para>
+ <para>
+ Wait event description
+ </para></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index af65af6bdd..f86a4dd770 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1342,3 +1342,6 @@ CREATE VIEW pg_stat_subscription_stats AS
ss.stats_reset
FROM pg_subscription as s,
pg_stat_get_subscription_stats(s.oid) as ss;
+
+CREATE VIEW pg_wait_event AS
+ SELECT * FROM pg_get_wait_events() AS we;
diff --git a/src/backend/utils/activity/.gitignore b/src/backend/utils/activity/.gitignore
index d77079285b..bd0c0c7772 100644
--- a/src/backend/utils/activity/.gitignore
+++ b/src/backend/utils/activity/.gitignore
@@ -1,2 +1,3 @@
/pgstat_wait_event.c
/wait_event_types.h
+/wait_event_funcs_data.c
diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile
index f1117745d4..289afe7c2d 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -32,10 +32,14 @@ OBJS = \
pgstat_subscription.o \
pgstat_wal.o \
pgstat_xact.o \
+ wait_event_funcs.o \
wait_event.o
include $(top_srcdir)/src/backend/common.mk
+wait_event_funcs.o: wait_event_funcs_data.c
+wait_event_funcs_data.c: wait_event_types.h
+
wait_event.o: pgstat_wait_event.c
pgstat_wait_event.c: wait_event_types.h
touch $@
@@ -44,4 +48,4 @@ wait_event_types.h: $(top_srcdir)/src/backend/utils/activity/wait_event_names.tx
$(PERL) $(srcdir)/generate-wait_event_types.pl --code $<
maintainer-clean: clean
- rm -f wait_event_types.h pgstat_wait_event.c
+ rm -f wait_event_types.h pgstat_wait_event.c wait_event_funcs_data.c
diff --git a/src/backend/utils/activity/generate-wait_event_types.pl b/src/backend/utils/activity/generate-wait_event_types.pl
index 56335e8730..6f4fcd22d2 100644
--- a/src/backend/utils/activity/generate-wait_event_types.pl
+++ b/src/backend/utils/activity/generate-wait_event_types.pl
@@ -4,6 +4,7 @@
# Generate wait events support files from wait_event_names.txt:
# - wait_event_types.h (if --code is passed)
# - pgstat_wait_event.c (if --code is passed)
+# - wait_event_funcs_data.c (if --code is passed)
# - wait_event_types.sgml (if --docs is passed)
#
# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
@@ -98,8 +99,10 @@ if ($gen_code)
# multiple times.
my $htmp = "$output_path/wait_event_types.h.tmp$$";
my $ctmp = "$output_path/pgstat_wait_event.c.tmp$$";
+ my $wctmp = "$output_path/wait_event_funcs_data.c.tmp$$";
open my $h, '>', $htmp or die "Could not open $htmp: $!";
open my $c, '>', $ctmp or die "Could not open $ctmp: $!";
+ open my $wc, '>', $wctmp or die "Could not open $wctmp: $!";
my $header_comment =
'/*-------------------------------------------------------------------------
@@ -129,12 +132,14 @@ if ($gen_code)
printf $c $header_comment, 'pgstat_wait_event.c';
+ printf $wc $header_comment, 'wait_event_funcs_data.c';
+
+ # Generate the pgstat_wait_event.c and wait_event_types.h files
# uc() is being used to force the comparison to be case-insensitive.
foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe)
{
-
- # Don't generate .c and .h files for Extension, LWLock and
- # Lock, these are handled independently.
+ # Don't generate the pgstat_wait_event.c and wait_event_types.h files
+ # for Extension, LWLock and Lock, these are handled independently.
next
if ( $waitclass eq 'WaitEventExtension'
|| $waitclass eq 'WaitEventLWLock'
@@ -183,14 +188,49 @@ if ($gen_code)
printf $c "}\n\n";
}
+ # Generate the wait_event_funcs_data.c file
+ # uc() is being used to force the comparison to be case-insensitive.
+ foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe)
+ {
+ my $last = $waitclass;
+ $last =~ s/^WaitEvent//;
+
+ foreach my $wev (@{ $hashwe{$waitclass} })
+ {
+ my $new_desc = substr $wev->[2], 1, -2;
+ # put an escape \ in front of '
+ $new_desc =~ s/'/\\'/g;
+
+ # just keep text from <whatever>text</wahtever>
+ $new_desc =~ s/<.*>(.*?)<.*>/$1/g;
+
+ # just keep text from <xref linkend="text"/>
+ $new_desc =~ s/<xref linkend="guc-(.*?)"\/>/$1/g;
+
+ # remove any "see <xref linkend="text"/>"
+ $new_desc =~ s/; see.*$//;
+
+ printf $wc "\telement = (wait_event_element *) palloc(sizeof(wait_event_element));\n";
+
+ printf $wc "\telement->wait_event_type = \"%s\";\n", $last;
+ printf $wc "\telement->wait_event_name = \"%s\";\n", $wev->[1];
+ printf $wc "\telement->wait_event_description = \"%s\";\n\n", $new_desc;
+
+ printf $wc "\twait_event = lappend(wait_event, element);\n\n";
+ }
+ }
+
printf $h "#endif /* WAIT_EVENT_TYPES_H */\n";
close $h;
close $c;
+ close $wc;
rename($htmp, "$output_path/wait_event_types.h")
|| die "rename: $htmp to $output_path/wait_event_types.h: $!";
rename($ctmp, "$output_path/pgstat_wait_event.c")
|| die "rename: $ctmp to $output_path/pgstat_wait_event.c: $!";
+ rename($wctmp, "$output_path/wait_event_funcs_data.c")
+ || die "rename: $ctmp to $output_path/wait_event_funcs_data.c: $!";
}
# Generate the .sgml file.
elsif ($gen_docs)
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 9633f3623c..46a27e7548 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -23,6 +23,7 @@ backend_sources += files(
# seems nicer to not add that as an include path for the whole backend.
waitevent_sources = files(
'wait_event.c',
+ 'wait_event_funcs.c',
)
wait_event = static_library('wait_event_names',
diff --git a/src/backend/utils/activity/wait_event_funcs.c b/src/backend/utils/activity/wait_event_funcs.c
new file mode 100644
index 0000000000..3e27a637e9
--- /dev/null
+++ b/src/backend/utils/activity/wait_event_funcs.c
@@ -0,0 +1,69 @@
+/* ----------
+ * wait_event_funcs.c
+ * Wait event reporting infrastructure.
+ *
+ * Copyright (c) 2001-2023, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/activity/wait_event_funcs.c
+ *
+ * NOTES
+ *
+ * The "wait_event_funcs_data.c" included in this file is generated by
+ * src/backend/utils/activity/generate-wait_event_types.pl
+ *
+ */
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+
+/*
+ * This function lists the wait events and their descriptions.
+ *
+ * The system view pg_wait_event provides a user interface to this
+ * SRF.
+ */
+Datum
+pg_get_wait_events(PG_FUNCTION_ARGS)
+{
+#define NUM_WAIT_EVENT_TABLES_ELEM 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ Datum values[NUM_WAIT_EVENT_TABLES_ELEM];
+ bool nulls[NUM_WAIT_EVENT_TABLES_ELEM];
+ List *wait_event = NIL;
+ ListCell *cell;
+
+ typedef struct
+ {
+ char *wait_event_type;
+ char *wait_event_name;
+ char *wait_event_description;
+ } wait_event_element;
+
+ wait_event_element *element = NULL;
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ /* Populate the wait_event List */
+ #include "wait_event_funcs_data.c"
+
+ /* Iterate over the list of wait events */
+ foreach(cell, wait_event)
+ {
+
+ wait_event_element *elem = lfirst(cell);
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+
+ values[0] = CStringGetTextDatum(elem->wait_event_type);
+ values[1] = CStringGetTextDatum(elem->wait_event_name);
+ values[2] = CStringGetTextDatum(elem->wait_event_description);
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6996073989..6d10d2e5be 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5417,6 +5417,12 @@
proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_delegation,leader_pid,query_id}',
prosrc => 'pg_stat_get_activity' },
+{ oid => '8403', descr => 'describe wait events',
+ proname => 'pg_get_wait_events', procost => '10', prorows => '100',
+ proretset => 't', provolatile => 's', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,text}',
+ proargmodes => '{o,o,o}', proargnames => '{wait_event_type,wait_event_name,description}',
+ prosrc => 'pg_get_wait_events' },
{ oid => '3318',
descr => 'statistics: information about progress of backends running maintenance command',
proname => 'pg_stat_get_progress_info', prorows => '100', proretset => 't',
diff --git a/src/include/utils/meson.build b/src/include/utils/meson.build
index 6de5d93799..c179478611 100644
--- a/src/include/utils/meson.build
+++ b/src/include/utils/meson.build
@@ -1,6 +1,6 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c']
+wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c', 'wait_event_funcs_data.c']
wait_event_target = custom_target('wait_event_names',
input: files('../../backend/utils/activity/wait_event_names.txt'),
output: wait_event_output,
@@ -11,7 +11,7 @@ wait_event_target = custom_target('wait_event_names',
],
build_by_default: true,
install: true,
- install_dir: [dir_include_server / 'utils', false],
+ install_dir: [dir_include_server / 'utils', false, false],
)
wait_event_types_h = wait_event_target[0]
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e07afcd4aa..f160cb5642 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2631,6 +2631,10 @@ pg_views| SELECT n.nspname AS schemaname,
FROM (pg_class c
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE (c.relkind = 'v'::"char");
+pg_wait_event| SELECT wait_event_type,
+ wait_event_name,
+ description
+ FROM pg_get_wait_events() we(wait_event_type, wait_event_name, description);
SELECT tablename, rulename, definition FROM pg_rules
WHERE schemaname = 'pg_catalog'
ORDER BY tablename, rulename;
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 001c6e7eb9..7ace77d714 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -134,6 +134,13 @@ select name, setting from pg_settings where name like 'enable%';
enable_tidscan | on
(21 rows)
+-- There is wait event descriptions
+select count(*) > 0 as ok FROM pg_wait_event;
+ ok
+----
+ t
+(1 row)
+
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail
-- without the outputs changing anytime IANA updates the underlying data,
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 351e469c77..643dfcc1d3 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -55,6 +55,9 @@ select count(*) = 0 as ok from pg_stat_wal_receiver;
-- a regression test run.
select name, setting from pg_settings where name like 'enable%';
+-- There is wait event descriptions
+select count(*) > 0 as ok FROM pg_wait_event;
+
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail
-- without the outputs changing anytime IANA updates the underlying data,
diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat
index 7cb23ea894..ac8da581e4 100755
--- a/src/tools/msvc/clean.bat
+++ b/src/tools/msvc/clean.bat
@@ -55,6 +55,7 @@ if exist src\include\catalog\header-stamp del /q src\include\catalog\header-stam
if exist doc\src\sgml\version.sgml del /q doc\src\sgml\version.sgml
if %DIST%==1 if exist src\backend\utils\activity\pgstat_wait_event.c del /q src\backend\utils\activity\pgstat_wait_event.c
+if %DIST%==1 if exist src\backend\utils\activity\wait_event_funcs_data.c del /q src\backend\utils\activity\wait_event_funcs_data.c
if %DIST%==1 if exist src\backend\utils\activity\wait_event_types.h del /q src\backend\utils\activity\wait_event_types.h
if %DIST%==1 if exist src\backend\utils\fmgroids.h del /q src\backend\utils\fmgroids.h
if %DIST%==1 if exist src\backend\utils\fmgrprotos.h del /q src\backend\utils\fmgrprotos.h
--
2.34.1
Attachments:
[text/plain] v4-0001-pg_wait_event.patch (15.5K, ../../[email protected]/2-v4-0001-pg_wait_event.patch)
download | inline diff:
From aa3e81810daaba7b1b2e05219ef662f2fd0607f8 Mon Sep 17 00:00:00 2001
From: bdrouvotAWS <[email protected]>
Date: Sat, 5 Aug 2023 12:39:42 +0000
Subject: [PATCH v4] pg_wait_event
Adding a new system view, namely pg_wait_event, that describes the wait events.
---
doc/src/sgml/system-views.sgml | 64 +++++++++++++++++
src/backend/catalog/system_views.sql | 3 +
src/backend/utils/activity/.gitignore | 1 +
src/backend/utils/activity/Makefile | 6 +-
.../activity/generate-wait_event_types.pl | 46 ++++++++++++-
src/backend/utils/activity/meson.build | 1 +
src/backend/utils/activity/wait_event_funcs.c | 69 +++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/meson.build | 4 +-
src/test/regress/expected/rules.out | 4 ++
src/test/regress/expected/sysviews.out | 7 ++
src/test/regress/sql/sysviews.sql | 3 +
src/tools/msvc/clean.bat | 1 +
13 files changed, 209 insertions(+), 6 deletions(-)
24.4% doc/src/sgml/
58.5% src/backend/utils/activity/
5.9% src/include/catalog/
4.1% src/test/regress/expected/
6.9% src/
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 57b228076e..ed26c8326f 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -221,6 +221,11 @@
<entry>views</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-wait-event"><structname>pg_wait_event</structname></link></entry>
+ <entry>wait events</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -4825,4 +4830,63 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+
+ <sect1 id="view-pg-wait-event">
+ <title><structname>pg_wait_event</structname></title>
+
+ <indexterm zone="view-pg-wait-event">
+ <primary>pg_wait_event</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_wait_event</structname> provides description about the
+ wait events.
+ </para>
+
+ <table>
+ <title><structname>pg_wait_event</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>wait_event_type</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event type
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>wait_event_name</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>description</structfield> <type>texte</type>
+ </para>
+ <para>
+ Wait event description
+ </para></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index af65af6bdd..f86a4dd770 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1342,3 +1342,6 @@ CREATE VIEW pg_stat_subscription_stats AS
ss.stats_reset
FROM pg_subscription as s,
pg_stat_get_subscription_stats(s.oid) as ss;
+
+CREATE VIEW pg_wait_event AS
+ SELECT * FROM pg_get_wait_events() AS we;
diff --git a/src/backend/utils/activity/.gitignore b/src/backend/utils/activity/.gitignore
index d77079285b..bd0c0c7772 100644
--- a/src/backend/utils/activity/.gitignore
+++ b/src/backend/utils/activity/.gitignore
@@ -1,2 +1,3 @@
/pgstat_wait_event.c
/wait_event_types.h
+/wait_event_funcs_data.c
diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile
index f1117745d4..289afe7c2d 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -32,10 +32,14 @@ OBJS = \
pgstat_subscription.o \
pgstat_wal.o \
pgstat_xact.o \
+ wait_event_funcs.o \
wait_event.o
include $(top_srcdir)/src/backend/common.mk
+wait_event_funcs.o: wait_event_funcs_data.c
+wait_event_funcs_data.c: wait_event_types.h
+
wait_event.o: pgstat_wait_event.c
pgstat_wait_event.c: wait_event_types.h
touch $@
@@ -44,4 +48,4 @@ wait_event_types.h: $(top_srcdir)/src/backend/utils/activity/wait_event_names.tx
$(PERL) $(srcdir)/generate-wait_event_types.pl --code $<
maintainer-clean: clean
- rm -f wait_event_types.h pgstat_wait_event.c
+ rm -f wait_event_types.h pgstat_wait_event.c wait_event_funcs_data.c
diff --git a/src/backend/utils/activity/generate-wait_event_types.pl b/src/backend/utils/activity/generate-wait_event_types.pl
index 56335e8730..6f4fcd22d2 100644
--- a/src/backend/utils/activity/generate-wait_event_types.pl
+++ b/src/backend/utils/activity/generate-wait_event_types.pl
@@ -4,6 +4,7 @@
# Generate wait events support files from wait_event_names.txt:
# - wait_event_types.h (if --code is passed)
# - pgstat_wait_event.c (if --code is passed)
+# - wait_event_funcs_data.c (if --code is passed)
# - wait_event_types.sgml (if --docs is passed)
#
# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
@@ -98,8 +99,10 @@ if ($gen_code)
# multiple times.
my $htmp = "$output_path/wait_event_types.h.tmp$$";
my $ctmp = "$output_path/pgstat_wait_event.c.tmp$$";
+ my $wctmp = "$output_path/wait_event_funcs_data.c.tmp$$";
open my $h, '>', $htmp or die "Could not open $htmp: $!";
open my $c, '>', $ctmp or die "Could not open $ctmp: $!";
+ open my $wc, '>', $wctmp or die "Could not open $wctmp: $!";
my $header_comment =
'/*-------------------------------------------------------------------------
@@ -129,12 +132,14 @@ if ($gen_code)
printf $c $header_comment, 'pgstat_wait_event.c';
+ printf $wc $header_comment, 'wait_event_funcs_data.c';
+
+ # Generate the pgstat_wait_event.c and wait_event_types.h files
# uc() is being used to force the comparison to be case-insensitive.
foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe)
{
-
- # Don't generate .c and .h files for Extension, LWLock and
- # Lock, these are handled independently.
+ # Don't generate the pgstat_wait_event.c and wait_event_types.h files
+ # for Extension, LWLock and Lock, these are handled independently.
next
if ( $waitclass eq 'WaitEventExtension'
|| $waitclass eq 'WaitEventLWLock'
@@ -183,14 +188,49 @@ if ($gen_code)
printf $c "}\n\n";
}
+ # Generate the wait_event_funcs_data.c file
+ # uc() is being used to force the comparison to be case-insensitive.
+ foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe)
+ {
+ my $last = $waitclass;
+ $last =~ s/^WaitEvent//;
+
+ foreach my $wev (@{ $hashwe{$waitclass} })
+ {
+ my $new_desc = substr $wev->[2], 1, -2;
+ # put an escape \ in front of '
+ $new_desc =~ s/'/\\'/g;
+
+ # just keep text from <whatever>text</wahtever>
+ $new_desc =~ s/<.*>(.*?)<.*>/$1/g;
+
+ # just keep text from <xref linkend="text"/>
+ $new_desc =~ s/<xref linkend="guc-(.*?)"\/>/$1/g;
+
+ # remove any "see <xref linkend="text"/>"
+ $new_desc =~ s/; see.*$//;
+
+ printf $wc "\telement = (wait_event_element *) palloc(sizeof(wait_event_element));\n";
+
+ printf $wc "\telement->wait_event_type = \"%s\";\n", $last;
+ printf $wc "\telement->wait_event_name = \"%s\";\n", $wev->[1];
+ printf $wc "\telement->wait_event_description = \"%s\";\n\n", $new_desc;
+
+ printf $wc "\twait_event = lappend(wait_event, element);\n\n";
+ }
+ }
+
printf $h "#endif /* WAIT_EVENT_TYPES_H */\n";
close $h;
close $c;
+ close $wc;
rename($htmp, "$output_path/wait_event_types.h")
|| die "rename: $htmp to $output_path/wait_event_types.h: $!";
rename($ctmp, "$output_path/pgstat_wait_event.c")
|| die "rename: $ctmp to $output_path/pgstat_wait_event.c: $!";
+ rename($wctmp, "$output_path/wait_event_funcs_data.c")
+ || die "rename: $ctmp to $output_path/wait_event_funcs_data.c: $!";
}
# Generate the .sgml file.
elsif ($gen_docs)
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 9633f3623c..46a27e7548 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -23,6 +23,7 @@ backend_sources += files(
# seems nicer to not add that as an include path for the whole backend.
waitevent_sources = files(
'wait_event.c',
+ 'wait_event_funcs.c',
)
wait_event = static_library('wait_event_names',
diff --git a/src/backend/utils/activity/wait_event_funcs.c b/src/backend/utils/activity/wait_event_funcs.c
new file mode 100644
index 0000000000..3e27a637e9
--- /dev/null
+++ b/src/backend/utils/activity/wait_event_funcs.c
@@ -0,0 +1,69 @@
+/* ----------
+ * wait_event_funcs.c
+ * Wait event reporting infrastructure.
+ *
+ * Copyright (c) 2001-2023, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/activity/wait_event_funcs.c
+ *
+ * NOTES
+ *
+ * The "wait_event_funcs_data.c" included in this file is generated by
+ * src/backend/utils/activity/generate-wait_event_types.pl
+ *
+ */
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+
+/*
+ * This function lists the wait events and their descriptions.
+ *
+ * The system view pg_wait_event provides a user interface to this
+ * SRF.
+ */
+Datum
+pg_get_wait_events(PG_FUNCTION_ARGS)
+{
+#define NUM_WAIT_EVENT_TABLES_ELEM 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ Datum values[NUM_WAIT_EVENT_TABLES_ELEM];
+ bool nulls[NUM_WAIT_EVENT_TABLES_ELEM];
+ List *wait_event = NIL;
+ ListCell *cell;
+
+ typedef struct
+ {
+ char *wait_event_type;
+ char *wait_event_name;
+ char *wait_event_description;
+ } wait_event_element;
+
+ wait_event_element *element = NULL;
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ /* Populate the wait_event List */
+ #include "wait_event_funcs_data.c"
+
+ /* Iterate over the list of wait events */
+ foreach(cell, wait_event)
+ {
+
+ wait_event_element *elem = lfirst(cell);
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+
+ values[0] = CStringGetTextDatum(elem->wait_event_type);
+ values[1] = CStringGetTextDatum(elem->wait_event_name);
+ values[2] = CStringGetTextDatum(elem->wait_event_description);
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6996073989..6d10d2e5be 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5417,6 +5417,12 @@
proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_delegation,leader_pid,query_id}',
prosrc => 'pg_stat_get_activity' },
+{ oid => '8403', descr => 'describe wait events',
+ proname => 'pg_get_wait_events', procost => '10', prorows => '100',
+ proretset => 't', provolatile => 's', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,text}',
+ proargmodes => '{o,o,o}', proargnames => '{wait_event_type,wait_event_name,description}',
+ prosrc => 'pg_get_wait_events' },
{ oid => '3318',
descr => 'statistics: information about progress of backends running maintenance command',
proname => 'pg_stat_get_progress_info', prorows => '100', proretset => 't',
diff --git a/src/include/utils/meson.build b/src/include/utils/meson.build
index 6de5d93799..c179478611 100644
--- a/src/include/utils/meson.build
+++ b/src/include/utils/meson.build
@@ -1,6 +1,6 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c']
+wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c', 'wait_event_funcs_data.c']
wait_event_target = custom_target('wait_event_names',
input: files('../../backend/utils/activity/wait_event_names.txt'),
output: wait_event_output,
@@ -11,7 +11,7 @@ wait_event_target = custom_target('wait_event_names',
],
build_by_default: true,
install: true,
- install_dir: [dir_include_server / 'utils', false],
+ install_dir: [dir_include_server / 'utils', false, false],
)
wait_event_types_h = wait_event_target[0]
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e07afcd4aa..f160cb5642 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2631,6 +2631,10 @@ pg_views| SELECT n.nspname AS schemaname,
FROM (pg_class c
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE (c.relkind = 'v'::"char");
+pg_wait_event| SELECT wait_event_type,
+ wait_event_name,
+ description
+ FROM pg_get_wait_events() we(wait_event_type, wait_event_name, description);
SELECT tablename, rulename, definition FROM pg_rules
WHERE schemaname = 'pg_catalog'
ORDER BY tablename, rulename;
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 001c6e7eb9..7ace77d714 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -134,6 +134,13 @@ select name, setting from pg_settings where name like 'enable%';
enable_tidscan | on
(21 rows)
+-- There is wait event descriptions
+select count(*) > 0 as ok FROM pg_wait_event;
+ ok
+----
+ t
+(1 row)
+
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail
-- without the outputs changing anytime IANA updates the underlying data,
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 351e469c77..643dfcc1d3 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -55,6 +55,9 @@ select count(*) = 0 as ok from pg_stat_wal_receiver;
-- a regression test run.
select name, setting from pg_settings where name like 'enable%';
+-- There is wait event descriptions
+select count(*) > 0 as ok FROM pg_wait_event;
+
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail
-- without the outputs changing anytime IANA updates the underlying data,
diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat
index 7cb23ea894..ac8da581e4 100755
--- a/src/tools/msvc/clean.bat
+++ b/src/tools/msvc/clean.bat
@@ -55,6 +55,7 @@ if exist src\include\catalog\header-stamp del /q src\include\catalog\header-stam
if exist doc\src\sgml\version.sgml del /q doc\src\sgml\version.sgml
if %DIST%==1 if exist src\backend\utils\activity\pgstat_wait_event.c del /q src\backend\utils\activity\pgstat_wait_event.c
+if %DIST%==1 if exist src\backend\utils\activity\wait_event_funcs_data.c del /q src\backend\utils\activity\wait_event_funcs_data.c
if %DIST%==1 if exist src\backend\utils\activity\wait_event_types.h del /q src\backend\utils\activity\wait_event_types.h
if %DIST%==1 if exist src\backend\utils\fmgroids.h del /q src\backend\utils\fmgroids.h
if %DIST%==1 if exist src\backend\utils\fmgrprotos.h del /q src\backend\utils\fmgrprotos.h
--
2.34.1
^ permalink raw reply [nested|flat] 7+ messages in thread
* Re: WIP: new system catalog pg_wait_event
2023-08-08 05:37 Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-08 08:16 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-09 07:56 ` Re: WIP: new system catalog pg_wait_event Michael Paquier <[email protected]>
2023-08-10 18:09 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
@ 2023-08-14 04:37 ` Michael Paquier <[email protected]>
2023-08-16 05:04 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
0 siblings, 1 reply; 7+ messages in thread
From: Michael Paquier @ 2023-08-14 04:37 UTC (permalink / raw)
To: Drouvot, Bertrand <[email protected]>; +Cc: Kyotaro Horiguchi <[email protected]>; [email protected]; [email protected]
On Thu, Aug 10, 2023 at 08:09:34PM +0200, Drouvot, Bertrand wrote:
> Agree that's worth it given the fact that iterating one more time is not that
> costly here.
I have reviewed v4, and finished by putting my hands on it to see what
I could do.
+ printf $wc "\telement = (wait_event_element *) palloc(sizeof(wait_event_element));\n";
+
+ printf $wc "\telement->wait_event_type = \"%s\";\n", $last;
+ printf $wc "\telement->wait_event_name = \"%s\";\n", $wev->[1];
+ printf $wc "\telement->wait_event_description = \"%s\";\n\n", $new_desc;
+
+ printf $wc "\twait_event = lappend(wait_event, element);\n\n";
+ }
This is simpler than the previous versions, still I am not much a fan
of implying the use of a list and these pallocs. There are two things
that we could do:
- Hide that behind a macro defined in wait_event_funcs.c.
- Feed the data generated here into a static structure, like:
+static const struct
+{
+ const char *type;
+ const char *name;
+ const char *description;
+}
After experimenting with both, I've found the latter a tad cleaner, so
the attached version does that.
+ <structfield>description</structfield> <type>texte</type>
This one was difficult to see..
I am not sure that "pg_wait_event" is a good idea for the name if the
new view. How about "pg_wait_events" instead, in plural form? There
is more than one wait event listed.
One log entry in Solution.pm has missed the addition of a reference to
wait_event_funcs_data.c.
And.. src/backend/Makefile missed two updates for maintainer-clean & co,
no?
One thing that the patch is still missing is the handling of custom
wait events for extensions. So this still requires more code. I was
thinking about listed these events as:
- Type: "Extension"
- Name: What a module has registered.
- Description: "Custom wait event \"%Name%\" defined by extension".
For now I am attaching a v5.
--
Michael
Attachments:
[text/x-diff] v5-0001-pg_wait_event.patch (17.8K, ../../[email protected]/2-v5-0001-pg_wait_event.patch)
download | inline diff:
From 4f0172e216bfa7b929b9ca3465d66088b2ac1566 Mon Sep 17 00:00:00 2001
From: bdrouvotAWS <[email protected]>
Date: Sat, 5 Aug 2023 12:39:42 +0000
Subject: [PATCH v5] Add catalog pg_wait_events
Adding a new system view, namely pg_wait_event, that describes the wait
events.
---
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/meson.build | 4 +-
src/backend/Makefile | 3 +-
src/backend/catalog/system_views.sql | 3 +
src/backend/utils/activity/.gitignore | 1 +
src/backend/utils/activity/Makefile | 8 ++-
.../activity/generate-wait_event_types.pl | 46 +++++++++++--
src/backend/utils/activity/meson.build | 1 +
src/backend/utils/activity/wait_event_funcs.c | 69 +++++++++++++++++++
src/test/regress/expected/rules.out | 4 ++
src/test/regress/expected/sysviews.out | 16 +++++
src/test/regress/sql/sysviews.sql | 4 ++
doc/src/sgml/system-views.sgml | 64 +++++++++++++++++
src/tools/msvc/Solution.pm | 3 +-
src/tools/msvc/clean.bat | 1 +
15 files changed, 223 insertions(+), 10 deletions(-)
create mode 100644 src/backend/utils/activity/wait_event_funcs.c
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6996073989..1a942c678c 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5417,6 +5417,12 @@
proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_delegation,leader_pid,query_id}',
prosrc => 'pg_stat_get_activity' },
+{ oid => '8403', descr => 'describe wait events',
+ proname => 'pg_get_wait_events', procost => '10', prorows => '100',
+ proretset => 't', provolatile => 's', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,text}',
+ proargmodes => '{o,o,o}', proargnames => '{type,name,description}',
+ prosrc => 'pg_get_wait_events' },
{ oid => '3318',
descr => 'statistics: information about progress of backends running maintenance command',
proname => 'pg_stat_get_progress_info', prorows => '100', proretset => 't',
diff --git a/src/include/utils/meson.build b/src/include/utils/meson.build
index 6de5d93799..c179478611 100644
--- a/src/include/utils/meson.build
+++ b/src/include/utils/meson.build
@@ -1,6 +1,6 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c']
+wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c', 'wait_event_funcs_data.c']
wait_event_target = custom_target('wait_event_names',
input: files('../../backend/utils/activity/wait_event_names.txt'),
output: wait_event_output,
@@ -11,7 +11,7 @@ wait_event_target = custom_target('wait_event_names',
],
build_by_default: true,
install: true,
- install_dir: [dir_include_server / 'utils', false],
+ install_dir: [dir_include_server / 'utils', false, false],
)
wait_event_types_h = wait_event_target[0]
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 1c929383c4..3e275ac759 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -134,7 +134,7 @@ storage/lmgr/lwlocknames.h: storage/lmgr/generate-lwlocknames.pl storage/lmgr/lw
$(MAKE) -C storage/lmgr lwlocknames.h lwlocknames.c
utils/activity/wait_event_types.h: utils/activity/generate-wait_event_types.pl utils/activity/wait_event_names.txt
- $(MAKE) -C utils/activity wait_event_types.h pgstat_wait_event.c
+ $(MAKE) -C utils/activity wait_event_types.h pgstat_wait_event.c wait_event_funcs_data.c
# run this unconditionally to avoid needing to know its dependencies here:
submake-catalog-headers:
@@ -311,6 +311,7 @@ maintainer-clean: distclean
storage/lmgr/lwlocknames.c \
storage/lmgr/lwlocknames.h \
utils/activity/pgstat_wait_event.c \
+ utils/activity/wait_event_funcs_data.c \
utils/activity/wait_event_types.h \
utils/adt/jsonpath_gram.c \
utils/adt/jsonpath_gram.h \
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index af65af6bdd..362b1ea8d5 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1342,3 +1342,6 @@ CREATE VIEW pg_stat_subscription_stats AS
ss.stats_reset
FROM pg_subscription as s,
pg_stat_get_subscription_stats(s.oid) as ss;
+
+CREATE VIEW pg_wait_events AS
+ SELECT * FROM pg_get_wait_events() AS we;
diff --git a/src/backend/utils/activity/.gitignore b/src/backend/utils/activity/.gitignore
index d77079285b..bd0c0c7772 100644
--- a/src/backend/utils/activity/.gitignore
+++ b/src/backend/utils/activity/.gitignore
@@ -1,2 +1,3 @@
/pgstat_wait_event.c
/wait_event_types.h
+/wait_event_funcs_data.c
diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile
index f1117745d4..f57cf3958c 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -32,10 +32,14 @@ OBJS = \
pgstat_subscription.o \
pgstat_wal.o \
pgstat_xact.o \
- wait_event.o
+ wait_event.o \
+ wait_event_funcs.o
include $(top_srcdir)/src/backend/common.mk
+wait_event_funcs.o: wait_event_funcs_data.c
+wait_event_funcs_data.c: wait_event_types.h
+
wait_event.o: pgstat_wait_event.c
pgstat_wait_event.c: wait_event_types.h
touch $@
@@ -44,4 +48,4 @@ wait_event_types.h: $(top_srcdir)/src/backend/utils/activity/wait_event_names.tx
$(PERL) $(srcdir)/generate-wait_event_types.pl --code $<
maintainer-clean: clean
- rm -f wait_event_types.h pgstat_wait_event.c
+ rm -f wait_event_types.h pgstat_wait_event.c wait_event_funcs_data.c
diff --git a/src/backend/utils/activity/generate-wait_event_types.pl b/src/backend/utils/activity/generate-wait_event_types.pl
index 56335e8730..6483966c6a 100644
--- a/src/backend/utils/activity/generate-wait_event_types.pl
+++ b/src/backend/utils/activity/generate-wait_event_types.pl
@@ -4,6 +4,7 @@
# Generate wait events support files from wait_event_names.txt:
# - wait_event_types.h (if --code is passed)
# - pgstat_wait_event.c (if --code is passed)
+# - wait_event_funcs_data.c (if --code is passed)
# - wait_event_types.sgml (if --docs is passed)
#
# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
@@ -98,8 +99,10 @@ if ($gen_code)
# multiple times.
my $htmp = "$output_path/wait_event_types.h.tmp$$";
my $ctmp = "$output_path/pgstat_wait_event.c.tmp$$";
+ my $wctmp = "$output_path/wait_event_funcs_data.c.tmp$$";
open my $h, '>', $htmp or die "Could not open $htmp: $!";
open my $c, '>', $ctmp or die "Could not open $ctmp: $!";
+ open my $wc, '>', $wctmp or die "Could not open $wctmp: $!";
my $header_comment =
'/*-------------------------------------------------------------------------
@@ -129,12 +132,14 @@ if ($gen_code)
printf $c $header_comment, 'pgstat_wait_event.c';
+ printf $wc $header_comment, 'wait_event_funcs_data.c';
+
+ # Generate the pgstat_wait_event.c and wait_event_types.h files
# uc() is being used to force the comparison to be case-insensitive.
foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe)
{
-
- # Don't generate .c and .h files for Extension, LWLock and
- # Lock, these are handled independently.
+ # Don't generate the pgstat_wait_event.c and wait_event_types.h files
+ # for Extension, LWLock and Lock, these are handled independently.
next
if ( $waitclass eq 'WaitEventExtension'
|| $waitclass eq 'WaitEventLWLock'
@@ -183,14 +188,47 @@ if ($gen_code)
printf $c "}\n\n";
}
+ # Generate wait_event_funcs_data.c, for the contents of static C
+ # structure holding all the information about the wait events.
+ # uc() is being used to force the comparison to be case-insensitive,
+ # even though it is not strictly mandatory here.
+ foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe)
+ {
+ my $last = $waitclass;
+ $last =~ s/^WaitEvent//;
+
+ foreach my $wev (@{ $hashwe{$waitclass} })
+ {
+ my $new_desc = substr $wev->[2], 1, -2;
+ # Escape single quotes.
+ $new_desc =~ s/'/\\'/g;
+
+ # Remove SGML markups
+ $new_desc =~ s/<.*>(.*?)<.*>/$1/g;
+
+ # Tweak contents about links <xref linkend="text"/> on GUCs,
+ # then remove any reference to "see <xref linkend="text"/>".
+ $new_desc =~ s/<xref linkend="guc-(.*?)"\/>/$1/g;
+ $new_desc =~ s/; see.*$//;
+
+ # One element to the C structure holding the wait event
+ # info, as of (type, name, description).
+ printf $wc "\t{\"%s\", \"%s\", \"%s\"},\n", $last, $wev->[1],
+ $new_desc;
+ }
+ }
+
printf $h "#endif /* WAIT_EVENT_TYPES_H */\n";
close $h;
close $c;
+ close $wc;
rename($htmp, "$output_path/wait_event_types.h")
|| die "rename: $htmp to $output_path/wait_event_types.h: $!";
rename($ctmp, "$output_path/pgstat_wait_event.c")
|| die "rename: $ctmp to $output_path/pgstat_wait_event.c: $!";
+ rename($wctmp, "$output_path/wait_event_funcs_data.c")
+ || die "rename: $ctmp to $output_path/wait_event_funcs_data.c: $!";
}
# Generate the .sgml file.
elsif ($gen_docs)
@@ -249,7 +287,7 @@ Usage: perl [--output <path>] [--code ] [ --sgml ] input_file
Options:
--outdir Output directory (default '.')
- --code Generate wait_event_types.h and pgstat_wait_event.c.
+ --code Generate C and header files.
--sgml Generate wait_event_types.sgml.
generate-wait_event_types.pl generates the SGML documentation and code
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 9633f3623c..46a27e7548 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -23,6 +23,7 @@ backend_sources += files(
# seems nicer to not add that as an include path for the whole backend.
waitevent_sources = files(
'wait_event.c',
+ 'wait_event_funcs.c',
)
wait_event = static_library('wait_event_names',
diff --git a/src/backend/utils/activity/wait_event_funcs.c b/src/backend/utils/activity/wait_event_funcs.c
new file mode 100644
index 0000000000..19d13c0dcd
--- /dev/null
+++ b/src/backend/utils/activity/wait_event_funcs.c
@@ -0,0 +1,69 @@
+/*------------------------------------------------------------------------
+ *
+ * wait_event_funcs.c
+ * Functions for accessing wait event data.
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/activity/wait_event_funcs.c
+ *
+ *------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+
+/*
+ * Each wait event has one corresponding entry in this structure, fed to
+ * the SQL function of this file.
+ */
+static const struct
+{
+ const char *type;
+ const char *name;
+ const char *description;
+}
+
+ waitEventData[] =
+{
+#include "wait_event_funcs_data.c"
+ /* end of list */
+ {NULL, NULL, NULL}
+};
+
+
+/*
+ * pg_get_wait_events
+ *
+ * List all the wait events (type, name) and their descriptions.
+ */
+Datum
+pg_get_wait_events(PG_FUNCTION_ARGS)
+{
+#define PG_GET_WAIT_EVENTS_COLS 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ /* Iterate over the list of wait events */
+ for (int idx = 0; waitEventData[idx].type != NULL; idx++)
+ {
+ Datum values[PG_GET_WAIT_EVENTS_COLS] = {0};
+ bool nulls[PG_GET_WAIT_EVENTS_COLS] = {0};
+
+ values[0] = CStringGetTextDatum(waitEventData[idx].type);
+ values[1] = CStringGetTextDatum(waitEventData[idx].name);
+ values[2] = CStringGetTextDatum(waitEventData[idx].description);
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ /* XXX: this needs to handle custom wait events, not yet in the tree */
+
+ return (Datum) 0;
+}
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e07afcd4aa..55447d8090 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2631,6 +2631,10 @@ pg_views| SELECT n.nspname AS schemaname,
FROM (pg_class c
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE (c.relkind = 'v'::"char");
+pg_wait_events| SELECT type,
+ name,
+ description
+ FROM pg_get_wait_events() we(type, name, description);
SELECT tablename, rulename, definition FROM pg_rules
WHERE schemaname = 'pg_catalog'
ORDER BY tablename, rulename;
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 001c6e7eb9..f3ac8dfe94 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -134,6 +134,22 @@ select name, setting from pg_settings where name like 'enable%';
enable_tidscan | on
(21 rows)
+-- There are always wait event descriptions for various types.
+select type, count(*) > 0 as ok FROM pg_wait_events
+ group by type order by type;
+ type | ok
+-----------+----
+ Activity | t
+ BufferPin | t
+ Client | t
+ Extension | t
+ IO | t
+ IPC | t
+ Lock | t
+ LWLock | t
+ Timeout | t
+(9 rows)
+
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail
-- without the outputs changing anytime IANA updates the underlying data,
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 351e469c77..853daa85eb 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -55,6 +55,10 @@ select count(*) = 0 as ok from pg_stat_wal_receiver;
-- a regression test run.
select name, setting from pg_settings where name like 'enable%';
+-- There are always wait event descriptions for various types.
+select type, count(*) > 0 as ok FROM pg_wait_events
+ group by type order by type;
+
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
-- more-or-less working. We can't test their contents in any great detail
-- without the outputs changing anytime IANA updates the underlying data,
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 57b228076e..2b35c2f91b 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -221,6 +221,11 @@
<entry>views</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-wait-events"><structname>pg_wait_events</structname></link></entry>
+ <entry>wait events</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -4825,4 +4830,63 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+
+ <sect1 id="view-pg-wait-events">
+ <title><structname>pg_wait_events</structname></title>
+
+ <indexterm zone="view-pg-wait-events">
+ <primary>pg_wait_events</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_wait_events</structname> provides description about the
+ wait events.
+ </para>
+
+ <table>
+ <title><structname>pg_wait_events</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>type</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event type
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>name</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>description</structfield> <type>text</type>
+ </para>
+ <para>
+ Wait event description
+ </para></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index c98a1e9f9a..a50f730260 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -588,7 +588,8 @@ sub GenerateFiles
'src/include/utils/wait_event_types.h',
'src/backend/utils/activity/wait_event_names.txt'))
{
- print "Generating pgstat_wait_event.c and wait_event_types.h...\n";
+ print
+ "Generating pgstat_wait_event.c, wait_event_types.h and wait_event_funcs_data.c...\n";
my $activ = 'src/backend/utils/activity';
system(
"perl $activ/generate-wait_event_types.pl --outdir $activ --code $activ/wait_event_names.txt"
diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat
index 7cb23ea894..ac8da581e4 100755
--- a/src/tools/msvc/clean.bat
+++ b/src/tools/msvc/clean.bat
@@ -55,6 +55,7 @@ if exist src\include\catalog\header-stamp del /q src\include\catalog\header-stam
if exist doc\src\sgml\version.sgml del /q doc\src\sgml\version.sgml
if %DIST%==1 if exist src\backend\utils\activity\pgstat_wait_event.c del /q src\backend\utils\activity\pgstat_wait_event.c
+if %DIST%==1 if exist src\backend\utils\activity\wait_event_funcs_data.c del /q src\backend\utils\activity\wait_event_funcs_data.c
if %DIST%==1 if exist src\backend\utils\activity\wait_event_types.h del /q src\backend\utils\activity\wait_event_types.h
if %DIST%==1 if exist src\backend\utils\fmgroids.h del /q src\backend\utils\fmgroids.h
if %DIST%==1 if exist src\backend\utils\fmgrprotos.h del /q src\backend\utils\fmgrprotos.h
--
2.40.1
[application/pgp-signature] signature.asc (833B, ../../[email protected]/3-signature.asc)
download
^ permalink raw reply [nested|flat] 7+ messages in thread
* Re: WIP: new system catalog pg_wait_event
2023-08-08 05:37 Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-08 08:16 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-09 07:56 ` Re: WIP: new system catalog pg_wait_event Michael Paquier <[email protected]>
2023-08-10 18:09 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-14 04:37 ` Re: WIP: new system catalog pg_wait_event Michael Paquier <[email protected]>
@ 2023-08-16 05:04 ` Drouvot, Bertrand <[email protected]>
0 siblings, 0 replies; 7+ messages in thread
From: Drouvot, Bertrand @ 2023-08-16 05:04 UTC (permalink / raw)
To: Michael Paquier <[email protected]>; +Cc: Kyotaro Horiguchi <[email protected]>; [email protected]; [email protected]
Hi,
On 8/14/23 6:37 AM, Michael Paquier wrote:
> On Thu, Aug 10, 2023 at 08:09:34PM +0200, Drouvot, Bertrand wrote:
>> Agree that's worth it given the fact that iterating one more time is not that
>> costly here.
>
> I have reviewed v4, and finished by putting my hands on it to see what
> I could do.
Thanks!
> There are two things
> that we could do:
> - Hide that behind a macro defined in wait_event_funcs.c.
> - Feed the data generated here into a static structure, like:
> +static const struct
> +{
> + const char *type;
> + const char *name;
> + const char *description;
> +}
>
> After experimenting with both, I've found the latter a tad cleaner, so
> the attached version does that.
Yeah, looking at what you've done in v5, I also agree that's better
that what has been done in v4 (I also think it will be easier to maintain).
> I am not sure that "pg_wait_event" is a good idea for the name if the
> new view. How about "pg_wait_events" instead, in plural form? There
> is more than one wait event listed.
>
I'd prefer the singular form. There is a lot of places where it's already used
(pg_database, pg_user, pg_namespace...to name a few) and it looks like that using
the plural form are exceptions.
> One log entry in Solution.pm has missed the addition of a reference to
> wait_event_funcs_data.c.
>
Oh right, thanks for fixing it in v5.
> And.. src/backend/Makefile missed two updates for maintainer-clean & co,
> no?
>
Oh right, thanks for fixing it in v5.
> One thing that the patch is still missing is the handling of custom
> wait events for extensions.
Yeah, now that af720b4c50 is done, I'll add the custom wait events handling
in v6.
> So this still requires more code. I was
> thinking about listed these events as:
> - Type: "Extension"
> - Name: What a module has registered.
> - Description: "Custom wait event \"%Name%\" defined by extension".
That sounds good to me, I'll do that.
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 7+ messages in thread
end of thread, other threads:[~2023-08-16 05:04 UTC | newest]
Thread overview: 7+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2021-02-03 18:00 [PATCH 5/9] BRIN minmax-multi indexes Tomas Vondra <[email protected]>
2023-08-08 05:37 Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-08 08:16 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-09 07:56 ` Re: WIP: new system catalog pg_wait_event Michael Paquier <[email protected]>
2023-08-10 18:09 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
2023-08-14 04:37 ` Re: WIP: new system catalog pg_wait_event Michael Paquier <[email protected]>
2023-08-16 05:04 ` Re: WIP: new system catalog pg_wait_event Drouvot, Bertrand <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox