[PATCH] Add contrib/anyarray: intarray-style operations and indexes for any array type

From: Влад Медведицков <vladmedveditskov(at)yandex(dot)ru>
To: "pgsql-hackers(at)lists(dot)postgresql(dot)org" <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: [PATCH] Add contrib/anyarray: intarray-style operations and indexes for any array type
Date: 2026-05-18 10:17:39
Message-ID: 9231779099364@mail.yandex.ru
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

<div><div><div>From e796800a32277a29013354344ede221b89e46c27 Mon Sep 17 00:00:00 2001</div><div>From: Vlad Medveditskov &lt;vladmedveditskov(at)yandex(dot)ru&gt;</div><div>Date: Mon, 18 May 2026 12:50:13 +0300</div><div>Subject: [PATCH] Add contrib/anyarray: intarray-style operations and indexes</div><div> for any array type</div><div> </div><div>This new contrib module generalizes contrib/intarray to arrays of any</div><div>element type with a default btree opclass.  It provides:</div><div> </div><div>* Set-style helpers for anyarray / anyelement: anyarray_sort</div><div>  (with optional asc/desc direction), anyarray_uniq, anyarray_idx,</div><div>  anyarray_subarray (2- and 3-arg), anyarray_icount,</div><div>  anyarray_intersect, anyarray_union, anyarray_union_elem,</div><div>  anyarray_difference, anyarray_difference_elem, plus the</div><div>  corresponding operators #, &amp;, |, - on anyarray and anyelement.</div><div> </div><div>* A textual boolean query type "anyquery" and the operators</div><div>  anyarray @@ anyquery (with commutator anyquery ~~ anyarray).</div><div>  Tokens are stored as text and parsed lazily through the</div><div>  element type's input function at match time, so the same</div><div>  query value can be used against arrays of different element</div><div>  types.</div><div> </div><div>* A signature-based polymorphic GiST opclass "anyarray_gist_ops"</div><div>  on anyarray.  Each indexed array is hashed into a fixed-size</div><div>  bit vector; internal nodes store the bitwise union of their</div><div>  children, with an ALLISTRUE short-circuit when every bit is</div><div>  set.  Signature length is configurable via the siglen</div><div>  reloption (default 252 bytes).</div><div> </div><div>* GIN opclasses for the three element types most commonly</div><div>  requested by users: int8_anyquery_gin_ops,</div><div>  uuid_anyquery_gin_ops and text_anyquery_gin_ops.  They are</div><div>  per-concrete-type because the GIN AM does not propagate</div><div>  fn_expr into extractQuery, so the element type needed to</div><div>  parse anyquery tokens cannot be discovered dynamically.</div><div>  Strategies 1-4 mirror the built-in array_ops; strategy 5</div><div>  is @@.</div><div> </div><div>* doc/src/sgml/anyarray.sgml with descriptions of every</div><div>  function, operator and opclass, plus a usage example.</div><div> </div><div>* Regression test (contrib/anyarray/sql/anyarray.sql,</div><div>  expected/anyarray.out) covering int8, uuid and text inputs,</div><div>  the error paths, and a cross-AM consistency block that</div><div>  asserts seqscan, GiST and GIN return the same row set for</div><div>  seven representative predicates.</div><div>---</div><div> contrib/Makefile                       |   1 +</div><div> contrib/anyarray/.gitignore            |   4 +</div><div> contrib/anyarray/Makefile              |  27 +</div><div> contrib/anyarray/anyarray--1.0.sql     | 336 +++++++++++</div><div> contrib/anyarray/anyarray.c            | 116 ++++</div><div> contrib/anyarray/anyarray.control      |   6 +</div><div> contrib/anyarray/anyarray.h            | 184 ++++++</div><div> contrib/anyarray/anyarray_bool.c       | 664 ++++++++++++++++++++++</div><div> contrib/anyarray/anyarray_gin.c        | 374 +++++++++++++</div><div> contrib/anyarray/anyarray_gist.c       | 742 +++++++++++++++++++++++++</div><div> contrib/anyarray/anyarray_op.c         | 613 ++++++++++++++++++++</div><div> contrib/anyarray/expected/anyarray.out | 727 ++++++++++++++++++++++++</div><div> contrib/anyarray/meson.build           |  38 ++</div><div> contrib/anyarray/sql/anyarray.sql      | 330 +++++++++++</div><div> contrib/meson.build                    |   1 +</div><div> doc/src/sgml/anyarray.sgml             | 482 ++++++++++++++++</div><div> doc/src/sgml/contrib.sgml              |   1 +</div><div> doc/src/sgml/filelist.sgml             |   1 +</div><div> src/test/regress/regression.diffs      |   0</div><div> src/test/regress/regression.out        |   2 +</div><div> src/tools/pgindent/typedefs.list       |   8 +</div><div> 21 files changed, 4657 insertions(+)</div><div> create mode 100644 contrib/anyarray/.gitignore</div><div> create mode 100644 contrib/anyarray/Makefile</div><div> create mode 100644 contrib/anyarray/anyarray--1.0.sql</div><div> create mode 100644 contrib/anyarray/anyarray.c</div><div> create mode 100644 contrib/anyarray/anyarray.control</div><div> create mode 100644 contrib/anyarray/anyarray.h</div><div> create mode 100644 contrib/anyarray/anyarray_bool.c</div><div> create mode 100644 contrib/anyarray/anyarray_gin.c</div><div> create mode 100644 contrib/anyarray/anyarray_gist.c</div><div> create mode 100644 contrib/anyarray/anyarray_op.c</div><div> create mode 100644 contrib/anyarray/expected/anyarray.out</div><div> create mode 100644 contrib/anyarray/meson.build</div><div> create mode 100644 contrib/anyarray/sql/anyarray.sql</div><div> create mode 100644 doc/src/sgml/anyarray.sgml</div><div> create mode 100644 src/test/regress/regression.diffs</div><div> create mode 100644 src/test/regress/regression.out</div><div> </div><div>diff --git a/contrib/Makefile b/contrib/Makefile</div><div>index 7d91fe77db3..b9586cb3a6b 100644</div><div>--- a/contrib/Makefile</div><div>+++ b/contrib/Makefile</div><div>@@ -6,6 +6,7 @@ include $(top_builddir)/src/Makefile.global</div><div> </div><div> SUBDIRS = \</div><div>         amcheck        \</div><div>+        anyarray    \</div><div>         auth_delay    \</div><div>         auto_explain    \</div><div>         basic_archive    \</div><div>diff --git a/contrib/anyarray/.gitignore b/contrib/anyarray/.gitignore</div><div>new file mode 100644</div><div>index 00000000000..5dcb3ff9723</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/.gitignore</div><div>@@ -0,0 +1,4 @@</div><div>+# Generated subdirectories</div><div>+/log/</div><div>+/results/</div><div>+/tmp_check/</div><div>diff --git a/contrib/anyarray/Makefile b/contrib/anyarray/Makefile</div><div>new file mode 100644</div><div>index 00000000000..d6eb9ae10ad</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/Makefile</div><div>@@ -0,0 +1,27 @@</div><div>+# contrib/anyarray/Makefile</div><div>+</div><div>+MODULE_big = anyarray</div><div>+OBJS = \</div><div>+    $(WIN32RES) \</div><div>+    anyarray.o \</div><div>+    anyarray_bool.o \</div><div>+    anyarray_gin.o \</div><div>+    anyarray_gist.o \</div><div>+    anyarray_op.o</div><div>+</div><div>+EXTENSION = anyarray</div><div>+DATA = anyarray--1.0.sql</div><div>+PGFILEDESC = "anyarray - operations and indexes for arrays of any type"</div><div>+</div><div>+REGRESS = anyarray</div><div>+</div><div>+ifdef USE_PGXS</div><div>+PG_CONFIG = pg_config</div><div>+PGXS := $(shell $(PG_CONFIG) --pgxs)</div><div>+include $(PGXS)</div><div>+else</div><div>+subdir = contrib/anyarray</div><div>+top_builddir = ../..</div><div>+include $(top_builddir)/src/Makefile.global</div><div>+include $(top_srcdir)/contrib/contrib-global.mk</div><div>+endif</div><div>diff --git a/contrib/anyarray/anyarray--1.0.sql b/contrib/anyarray/anyarray--1.0.sql</div><div>new file mode 100644</div><div>index 00000000000..84bcff34b5b</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/anyarray--1.0.sql</div><div>@@ -0,0 +1,336 @@</div><div>+/* contrib/anyarray/anyarray--1.0.sql */</div><div>+</div><div>+-- complain if script is sourced in psql, rather than via CREATE EXTENSION</div><div>+\echo Use "CREATE EXTENSION anyarray" to load this file. \quit</div><div>+</div><div>+--</div><div>+-- Set-style operations for arrays of any btree-orderable element type.</div><div>+--</div><div>+</div><div>+CREATE FUNCTION anyarray_sort(anyarray)</div><div>+RETURNS anyarray</div><div>+AS 'MODULE_PATHNAME', 'anyarray_sort'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_sort(anyarray, text)</div><div>+RETURNS anyarray</div><div>+AS 'MODULE_PATHNAME', 'anyarray_sort_dir'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_uniq(anyarray)</div><div>+RETURNS anyarray</div><div>+AS 'MODULE_PATHNAME', 'anyarray_uniq'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_idx(anyarray, anyelement)</div><div>+RETURNS int4</div><div>+AS 'MODULE_PATHNAME', 'anyarray_idx'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_subarray(anyarray, int4, int4)</div><div>+RETURNS anyarray</div><div>+AS 'MODULE_PATHNAME', 'anyarray_subarray'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_subarray(anyarray, int4)</div><div>+RETURNS anyarray</div><div>+AS 'MODULE_PATHNAME', 'anyarray_subarray_to_end'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_icount(anyarray)</div><div>+RETURNS int4</div><div>+AS 'MODULE_PATHNAME', 'anyarray_icount'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_intersect(anyarray, anyarray)</div><div>+RETURNS anyarray</div><div>+AS 'MODULE_PATHNAME', 'anyarray_intersect'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_union(anyarray, anyarray)</div><div>+RETURNS anyarray</div><div>+AS 'MODULE_PATHNAME', 'anyarray_union'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_union_elem(anyarray, anyelement)</div><div>+RETURNS anyarray</div><div>+AS 'MODULE_PATHNAME', 'anyarray_union_elem'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_difference(anyarray, anyarray)</div><div>+RETURNS anyarray</div><div>+AS 'MODULE_PATHNAME', 'anyarray_difference'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_difference_elem(anyarray, anyelement)</div><div>+RETURNS anyarray</div><div>+AS 'MODULE_PATHNAME', 'anyarray_difference_elem'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+--</div><div>+-- Operators</div><div>+--</div><div>+</div><div>+-- Unary prefix # : cardinality (number of elements)</div><div>+CREATE OPERATOR # (</div><div>+    RIGHTARG = anyarray,</div><div>+    PROCEDURE = anyarray_icount</div><div>+);</div><div>+</div><div>+-- array # element : 1-based index of element, or 0 if not found</div><div>+CREATE OPERATOR # (</div><div>+    LEFTARG = anyarray,</div><div>+    RIGHTARG = anyelement,</div><div>+    PROCEDURE = anyarray_idx</div><div>+);</div><div>+</div><div>+-- Set intersection</div><div>+CREATE OPERATOR &amp; (</div><div>+    LEFTARG = anyarray,</div><div>+    RIGHTARG = anyarray,</div><div>+    COMMUTATOR = &amp;,</div><div>+    PROCEDURE = anyarray_intersect</div><div>+);</div><div>+</div><div>+-- Set union (array | array)</div><div>+CREATE OPERATOR | (</div><div>+    LEFTARG = anyarray,</div><div>+    RIGHTARG = anyarray,</div><div>+    COMMUTATOR = |,</div><div>+    PROCEDURE = anyarray_union</div><div>+);</div><div>+</div><div>+-- Append element (array | element)</div><div>+CREATE OPERATOR | (</div><div>+    LEFTARG = anyarray,</div><div>+    RIGHTARG = anyelement,</div><div>+    PROCEDURE = anyarray_union_elem</div><div>+);</div><div>+</div><div>+-- Set difference (array - array)</div><div>+CREATE OPERATOR - (</div><div>+    LEFTARG = anyarray,</div><div>+    RIGHTARG = anyarray,</div><div>+    PROCEDURE = anyarray_difference</div><div>+);</div><div>+</div><div>+-- Remove all occurrences of element (array - element)</div><div>+CREATE OPERATOR - (</div><div>+    LEFTARG = anyarray,</div><div>+    RIGHTARG = anyelement,</div><div>+    PROCEDURE = anyarray_difference_elem</div><div>+);</div><div>+</div><div>+--</div><div>+-- Boolean query type</div><div>+--</div><div>+</div><div>+CREATE FUNCTION anyquery_in(cstring)</div><div>+RETURNS anyquery</div><div>+AS 'MODULE_PATHNAME', 'anyquery_in'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyquery_out(anyquery)</div><div>+RETURNS cstring</div><div>+AS 'MODULE_PATHNAME', 'anyquery_out'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE TYPE anyquery (</div><div>+    INTERNALLENGTH = -1,</div><div>+    INPUT = anyquery_in,</div><div>+    OUTPUT = anyquery_out,</div><div>+    STORAGE = extended</div><div>+);</div><div>+</div><div>+CREATE FUNCTION anyquery_querytree(anyquery)</div><div>+RETURNS text</div><div>+AS 'MODULE_PATHNAME', 'anyquery_querytree'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_boolop(anyarray, anyquery)</div><div>+RETURNS bool</div><div>+AS 'MODULE_PATHNAME', 'anyarray_boolop'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyquery_boolop_rev(anyquery, anyarray)</div><div>+RETURNS bool</div><div>+AS 'MODULE_PATHNAME', 'anyquery_boolop_rev'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE OPERATOR @@ (</div><div>+    LEFTARG = anyarray,</div><div>+    RIGHTARG = anyquery,</div><div>+    PROCEDURE = anyarray_boolop,</div><div>+    COMMUTATOR = '~~',</div><div>+    RESTRICT = contsel,</div><div>+    JOIN = contjoinsel</div><div>+);</div><div>+</div><div>+CREATE OPERATOR ~~ (</div><div>+    LEFTARG = anyquery,</div><div>+    RIGHTARG = anyarray,</div><div>+    PROCEDURE = anyquery_boolop_rev,</div><div>+    COMMUTATOR = '@@',</div><div>+    RESTRICT = contsel,</div><div>+    JOIN = contjoinsel</div><div>+);</div><div>+</div><div>+--</div><div>+-- GiST opclass (signature-based)</div><div>+--</div><div>+</div><div>+CREATE FUNCTION anyarray_gist_key_in(cstring)</div><div>+RETURNS anyarray_gist_key</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gist_key_in'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gist_key_out(anyarray_gist_key)</div><div>+RETURNS cstring</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gist_key_out'</div><div>+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE TYPE anyarray_gist_key (</div><div>+    INTERNALLENGTH = -1,</div><div>+    INPUT = anyarray_gist_key_in,</div><div>+    OUTPUT = anyarray_gist_key_out</div><div>+);</div><div>+</div><div>+CREATE FUNCTION anyarray_gist_consistent(internal, anyarray, smallint, oid, internal)</div><div>+RETURNS bool</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gist_consistent'</div><div>+LANGUAGE C IMMUTABLE PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gist_compress(internal)</div><div>+RETURNS internal</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gist_compress'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gist_decompress(internal)</div><div>+RETURNS internal</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gist_decompress'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gist_union(internal, internal)</div><div>+RETURNS anyarray_gist_key</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gist_union'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gist_same(anyarray_gist_key, anyarray_gist_key, internal)</div><div>+RETURNS internal</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gist_same'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gist_penalty(internal, internal, internal)</div><div>+RETURNS internal</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gist_penalty'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gist_picksplit(internal, internal)</div><div>+RETURNS internal</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gist_picksplit'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gist_options(internal)</div><div>+RETURNS void</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gist_options'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE OPERATOR CLASS anyarray_gist_ops</div><div>+FOR TYPE anyarray USING gist</div><div>+AS</div><div>+    OPERATOR    3    &amp;&amp;,</div><div>+    OPERATOR    7    @&gt;,</div><div>+    OPERATOR    8    &lt;@,</div><div>+    OPERATOR    18    =,</div><div>+    OPERATOR    20    @@ (anyarray, anyquery),</div><div>+    FUNCTION    1    anyarray_gist_consistent(internal, anyarray, smallint, oid, internal),</div><div>+    FUNCTION    2    anyarray_gist_union(internal, internal),</div><div>+    FUNCTION    3    anyarray_gist_compress(internal),</div><div>+    FUNCTION    4    anyarray_gist_decompress(internal),</div><div>+    FUNCTION    5    anyarray_gist_penalty(internal, internal, internal),</div><div>+    FUNCTION    6    anyarray_gist_picksplit(internal, internal),</div><div>+    FUNCTION    7    anyarray_gist_same(anyarray_gist_key, anyarray_gist_key, internal),</div><div>+    FUNCTION    10    anyarray_gist_options(internal),</div><div>+    STORAGE        anyarray_gist_key;</div><div>+</div><div>+--</div><div>+-- GIN opclasses (per concrete element type, supporting standard ops + @@)</div><div>+--</div><div>+</div><div>+CREATE FUNCTION anyarray_gin_extract_query_int8(</div><div>+    int8[], internal, smallint, internal, internal, internal, internal)</div><div>+RETURNS internal</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gin_extract_query_int8'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gin_consistent_int8(</div><div>+    internal, smallint, int8[], integer, internal, internal, internal, internal)</div><div>+RETURNS bool</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gin_consistent_int8'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE OPERATOR CLASS int8_anyquery_gin_ops</div><div>+FOR TYPE int8[] USING gin</div><div>+AS</div><div>+    OPERATOR    1    &amp;&amp; (anyarray, anyarray),</div><div>+    OPERATOR    2    @&gt; (anyarray, anyarray),</div><div>+    OPERATOR    3    &lt;@ (anyarray, anyarray),</div><div>+    OPERATOR    4    = (anyarray, anyarray),</div><div>+    OPERATOR    5    @@ (anyarray, anyquery),</div><div>+    FUNCTION    1    btint8cmp(int8, int8),</div><div>+    FUNCTION    2    ginarrayextract(anyarray, internal, internal),</div><div>+    FUNCTION    3    anyarray_gin_extract_query_int8(int8[], internal, smallint, internal, internal, internal, internal),</div><div>+    FUNCTION    4    anyarray_gin_consistent_int8(internal, smallint, int8[], integer, internal, internal, internal, internal),</div><div>+    STORAGE        int8;</div><div>+</div><div>+CREATE FUNCTION anyarray_gin_extract_query_uuid(</div><div>+    uuid[], internal, smallint, internal, internal, internal, internal)</div><div>+RETURNS internal</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gin_extract_query_uuid'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gin_consistent_uuid(</div><div>+    internal, smallint, uuid[], integer, internal, internal, internal, internal)</div><div>+RETURNS bool</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gin_consistent_uuid'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE OPERATOR CLASS uuid_anyquery_gin_ops</div><div>+FOR TYPE uuid[] USING gin</div><div>+AS</div><div>+    OPERATOR    1    &amp;&amp; (anyarray, anyarray),</div><div>+    OPERATOR    2    @&gt; (anyarray, anyarray),</div><div>+    OPERATOR    3    &lt;@ (anyarray, anyarray),</div><div>+    OPERATOR    4    = (anyarray, anyarray),</div><div>+    OPERATOR    5    @@ (anyarray, anyquery),</div><div>+    FUNCTION    1    uuid_cmp(uuid, uuid),</div><div>+    FUNCTION    2    ginarrayextract(anyarray, internal, internal),</div><div>+    FUNCTION    3    anyarray_gin_extract_query_uuid(uuid[], internal, smallint, internal, internal, internal, internal),</div><div>+    FUNCTION    4    anyarray_gin_consistent_uuid(internal, smallint, uuid[], integer, internal, internal, internal, internal),</div><div>+    STORAGE        uuid;</div><div>+</div><div>+CREATE FUNCTION anyarray_gin_extract_query_text(</div><div>+    text[], internal, smallint, internal, internal, internal, internal)</div><div>+RETURNS internal</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gin_extract_query_text'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE FUNCTION anyarray_gin_consistent_text(</div><div>+    internal, smallint, text[], integer, internal, internal, internal, internal)</div><div>+RETURNS bool</div><div>+AS 'MODULE_PATHNAME', 'anyarray_gin_consistent_text'</div><div>+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;</div><div>+</div><div>+CREATE OPERATOR CLASS text_anyquery_gin_ops</div><div>+FOR TYPE text[] USING gin</div><div>+AS</div><div>+    OPERATOR    1    &amp;&amp; (anyarray, anyarray),</div><div>+    OPERATOR    2    @&gt; (anyarray, anyarray),</div><div>+    OPERATOR    3    &lt;@ (anyarray, anyarray),</div><div>+    OPERATOR    4    = (anyarray, anyarray),</div><div>+    OPERATOR    5    @@ (anyarray, anyquery),</div><div>+    FUNCTION    1    bttextcmp(text, text),</div><div>+    FUNCTION    2    ginarrayextract(anyarray, internal, internal),</div><div>+    FUNCTION    3    anyarray_gin_extract_query_text(text[], internal, smallint, internal, internal, internal, internal),</div><div>+    FUNCTION    4    anyarray_gin_consistent_text(internal, smallint, text[], integer, internal, internal, internal, internal),</div><div>+    STORAGE        text;</div><div>diff --git a/contrib/anyarray/anyarray.c b/contrib/anyarray/anyarray.c</div><div>new file mode 100644</div><div>index 00000000000..0a2ab2153c8</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/anyarray.c</div><div>@@ -0,0 +1,116 @@</div><div>+/*-------------------------------------------------------------------------</div><div>+ *</div><div>+ * anyarray.c</div><div>+ *        Common code for the anyarray extension.</div><div>+ *</div><div>+ * This extension extends PostgreSQL with intarray-style operations and</div><div>+ * index support that work for arrays of any element type with a default</div><div>+ * btree opclass.  It is a generalization of the contrib/intarray module.</div><div>+ *</div><div>+ * Copyright (c) 2026, PostgreSQL Global Development Group</div><div>+ *</div><div>+ * IDENTIFICATION</div><div>+ *        contrib/anyarray/anyarray.c</div><div>+ *</div><div>+ *-------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+#include "postgres.h"</div><div>+</div><div>+#include "anyarray.h"</div><div>+#include "catalog/pg_type.h"</div><div>+#include "utils/builtins.h"</div><div>+#include "utils/lsyscache.h"</div><div>+</div><div>+PG_MODULE_MAGIC_EXT(</div><div>+                    .name = "anyarray",</div><div>+                    .version = PG_VERSION</div><div>+);</div><div>+</div><div>+/*</div><div>+ * anyarray_get_meta</div><div>+ *        Fetch (and cache) type metadata for the given element type.</div><div>+ *</div><div>+ * The cache lives in fcinfo-&gt;flinfo-&gt;fn_extra so we pay the catalog lookup</div><div>+ * cost only once per call site.  If "need_hash" is true the element type</div><div>+ * must additionally provide a default hash function; otherwise only a btree</div><div>+ * comparison function is required.</div><div>+ */</div><div>+AnyArrayTypeInfo *</div><div>+anyarray_get_meta(FunctionCallInfo fcinfo, Oid element_type, bool need_hash)</div><div>+{<!-- --></div><div>+    AnyArrayTypeInfo *meta = (AnyArrayTypeInfo *) fcinfo-&gt;flinfo-&gt;fn_extra;</div><div>+    TypeCacheEntry *typentry;</div><div>+    uint32        flags;</div><div>+</div><div>+    if (meta == NULL)</div><div>+    {<!-- --></div><div>+        meta = (AnyArrayTypeInfo *)</div><div>+            MemoryContextAllocZero(fcinfo-&gt;flinfo-&gt;fn_mcxt,</div><div>+                                   sizeof(AnyArrayTypeInfo));</div><div>+        fcinfo-&gt;flinfo-&gt;fn_extra = meta;</div><div>+        meta-&gt;element_type = InvalidOid;</div><div>+    }</div><div>+</div><div>+    if (meta-&gt;element_type == element_type &amp;&amp; (!need_hash || meta-&gt;have_hash))</div><div>+        return meta;</div><div>+</div><div>+    flags = TYPECACHE_CMP_PROC_FINFO | TYPECACHE_EQ_OPR_FINFO;</div><div>+    if (need_hash)</div><div>+        flags |= TYPECACHE_HASH_PROC_FINFO;</div><div>+</div><div>+    typentry = lookup_type_cache(element_type, flags);</div><div>+</div><div>+    if (!OidIsValid(typentry-&gt;cmp_proc_finfo.fn_oid))</div><div>+        ereport(ERROR,</div><div>+                (errcode(ERRCODE_UNDEFINED_FUNCTION),</div><div>+                 errmsg("could not identify a comparison function for type %s",</div><div>+                        format_type_be(element_type))));</div><div>+</div><div>+    if (!OidIsValid(typentry-&gt;eq_opr_finfo.fn_oid))</div><div>+        ereport(ERROR,</div><div>+                (errcode(ERRCODE_UNDEFINED_FUNCTION),</div><div>+                 errmsg("could not identify an equality operator for type %s",</div><div>+                        format_type_be(element_type))));</div><div>+</div><div>+    if (need_hash &amp;&amp; !OidIsValid(typentry-&gt;hash_proc_finfo.fn_oid))</div><div>+        ereport(ERROR,</div><div>+                (errcode(ERRCODE_UNDEFINED_FUNCTION),</div><div>+                 errmsg("could not identify a hash function for type %s",</div><div>+                        format_type_be(element_type))));</div><div>+</div><div>+    meta-&gt;element_type = element_type;</div><div>+    get_typlenbyvalalign(element_type, &amp;meta-&gt;typlen, &amp;meta-&gt;typbyval,</div><div>+                         &amp;meta-&gt;typalign);</div><div>+    meta-&gt;typcollation = typentry-&gt;typcollation;</div><div>+    fmgr_info_cxt(typentry-&gt;cmp_proc_finfo.fn_oid, &amp;meta-&gt;cmp_proc,</div><div>+                  fcinfo-&gt;flinfo-&gt;fn_mcxt);</div><div>+    fmgr_info_cxt(typentry-&gt;eq_opr_finfo.fn_oid, &amp;meta-&gt;eq_proc,</div><div>+                  fcinfo-&gt;flinfo-&gt;fn_mcxt);</div><div>+    if (need_hash)</div><div>+    {<!-- --></div><div>+        fmgr_info_cxt(typentry-&gt;hash_proc_finfo.fn_oid, &amp;meta-&gt;hash_proc,</div><div>+                      fcinfo-&gt;flinfo-&gt;fn_mcxt);</div><div>+        meta-&gt;have_hash = true;</div><div>+    }</div><div>+    else</div><div>+        meta-&gt;have_hash = false;</div><div>+</div><div>+    return meta;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * anyarray_cmp_datum</div><div>+ *        qsort_arg-compatible comparator delegating to the type's btree cmp.</div><div>+ */</div><div>+int</div><div>+anyarray_cmp_datum(const void *a, const void *b, void *arg)</div><div>+{<!-- --></div><div>+    AnyArrayTypeInfo *meta = (AnyArrayTypeInfo *) arg;</div><div>+    Datum        d1 = *((const Datum *) a);</div><div>+    Datum        d2 = *((const Datum *) b);</div><div>+    Datum        result;</div><div>+</div><div>+    result = FunctionCall2Coll(&amp;meta-&gt;cmp_proc, meta-&gt;typcollation, d1, d2);</div><div>+    return DatumGetInt32(result);</div><div>+}</div><div>diff --git a/contrib/anyarray/anyarray.control b/contrib/anyarray/anyarray.control</div><div>new file mode 100644</div><div>index 00000000000..2478f2d93d7</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/anyarray.control</div><div>@@ -0,0 +1,6 @@</div><div>+# anyarray extension</div><div>+comment = 'Operations and indexes for arrays of any type'</div><div>+default_version = '1.0'</div><div>+module_pathname = '$libdir/anyarray'</div><div>+relocatable = true</div><div>+trusted = false</div><div>diff --git a/contrib/anyarray/anyarray.h b/contrib/anyarray/anyarray.h</div><div>new file mode 100644</div><div>index 00000000000..f59ac9de749</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/anyarray.h</div><div>@@ -0,0 +1,184 @@</div><div>+/*-------------------------------------------------------------------------</div><div>+ *</div><div>+ * anyarray.h</div><div>+ *        Shared declarations for the anyarray extension.</div><div>+ *</div><div>+ * Copyright (c) 2026, PostgreSQL Global Development Group</div><div>+ *</div><div>+ * IDENTIFICATION</div><div>+ *        contrib/anyarray/anyarray.h</div><div>+ *</div><div>+ *-------------------------------------------------------------------------</div><div>+ */</div><div>+#ifndef ANYARRAY_H</div><div>+#define ANYARRAY_H</div><div>+</div><div>+#include "access/gist.h"</div><div>+#include "fmgr.h"</div><div>+#include "utils/array.h"</div><div>+#include "utils/typcache.h"</div><div>+</div><div>+/*</div><div>+ * Per-call cached element type metadata.</div><div>+ *</div><div>+ * Stored in fcinfo-&gt;flinfo-&gt;fn_extra so we look up the type once per</div><div>+ * planner-level call site.  All entries are populated by anyarray_get_meta().</div><div>+ */</div><div>+typedef struct AnyArrayTypeInfo</div><div>+{<!-- --></div><div>+    Oid            element_type;    /* element OID, or InvalidOid if not</div><div>+                                 * initialized */</div><div>+    int16        typlen;</div><div>+    bool        typbyval;</div><div>+    char        typalign;</div><div>+    Oid            typcollation;    /* default collation for comparisons */</div><div>+    FmgrInfo    cmp_proc;        /* btree comparison function for element_type */</div><div>+    FmgrInfo    eq_proc;        /* equality function for element_type */</div><div>+    FmgrInfo    hash_proc;        /* hash function for element_type (optional) */</div><div>+    bool        have_hash;        /* whether hash_proc was filled in */</div><div>+} AnyArrayTypeInfo;</div><div>+</div><div>+/* Reject the kinds of input we do not handle. */</div><div>+#define ANYARRAY_CHECK_ARRAY(arr) \</div><div>+    do { \</div><div>+        if (ARR_NDIM(arr) &gt; 1) \</div><div>+            ereport(ERROR, \</div><div>+                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \</div><div>+                     errmsg("multidimensional arrays are not supported"))); \</div><div>+        if (ARR_HASNULL(arr) &amp;&amp; array_contains_nulls(arr)) \</div><div>+            ereport(ERROR, \</div><div>+                    (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), \</div><div>+                     errmsg("array must not contain nulls"))); \</div><div>+    } while (0)</div><div>+</div><div>+#define ANYARRAY_NELEMS(arr) \</div><div>+    (ARR_NDIM(arr) &gt; 0 ? ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)) : 0)</div><div>+</div><div>+/*</div><div>+ * Initialize / refresh AnyArrayTypeInfo for a given element type.</div><div>+ *</div><div>+ * Allocates the struct in fcinfo-&gt;flinfo-&gt;fn_mcxt on first use; on subsequent</div><div>+ * calls reuses the cache unless the element type changed.  Reports an error</div><div>+ * via ereport() if the element type lacks a btree comparison operator</div><div>+ * (need_hash == true additionally requires a hash function).</div><div>+ */</div><div>+extern AnyArrayTypeInfo *anyarray_get_meta(FunctionCallInfo fcinfo,</div><div>+                                           Oid element_type,</div><div>+                                           bool need_hash);</div><div>+</div><div>+/*</div><div>+ * Comparison helper used both as a qsort_arg callback and directly.</div><div>+ * "arg" must be a valid AnyArrayTypeInfo *.</div><div>+ */</div><div>+extern int    anyarray_cmp_datum(const void *a, const void *b, void *arg);</div><div>+</div><div>+/*****************************************************************************</div><div>+ *  Boolean query type</div><div>+ *</div><div>+ *  An anyquery value is a boolean expression in postfix notation whose leaves</div><div>+ *  are textual representations of values.  The element type is not known at</div><div>+ *  parse time; it is supplied at @@-time by the array operand, and the value</div><div>+ *  strings are parsed lazily through the element type's text input function.</div><div>+ *</div><div>+ *  On-disk layout:</div><div>+ *</div><div>+ *     +-----------------------------+</div><div>+ *     | varlena header              |</div><div>+ *     | int32  size      (n items)  |</div><div>+ *     | int32  str_off              |  -- byte offset to string heap</div><div>+ *     | int32  str_len              |</div><div>+ *     | AnyQueryItem items[size]    |</div><div>+ *     | &lt;padding&gt;                   |</div><div>+ *     | char   strings[str_len]     |  -- null-terminated value strings</div><div>+ *     +-----------------------------+</div><div>+ *****************************************************************************/</div><div>+</div><div>+/* item.type values */</div><div>+#define ANYQ_VAL    1</div><div>+#define ANYQ_OPR    2</div><div>+</div><div>+/* operator codes (also used in the input grammar) */</div><div>+#define ANYQ_AND    '&amp;'</div><div>+#define ANYQ_OR        '|'</div><div>+#define ANYQ_NOT    '!'</div><div>+</div><div>+typedef struct AnyQueryItem</div><div>+{<!-- --></div><div>+    int16        type;            /* ANYQ_VAL or ANYQ_OPR */</div><div>+    int16        left;            /* offset to left-operand item (OPR only) */</div><div>+    int32        payload;        /* VAL: offset into string heap; OPR: one of</div><div>+                                 * ANYQ_AND/OR/NOT */</div><div>+} AnyQueryItem;</div><div>+</div><div>+typedef struct AnyQuery</div><div>+{<!-- --></div><div>+    int32        vl_len_;        /* varlena header (do not touch directly!) */</div><div>+    int32        size;            /* number of items */</div><div>+    int32        str_off;        /* byte offset to string heap */</div><div>+    int32        str_len;        /* length of string heap */</div><div>+    AnyQueryItem items[FLEXIBLE_ARRAY_MEMBER];</div><div>+    /* followed by null-terminated value strings */</div><div>+} AnyQuery;</div><div>+</div><div>+#define ANYQUERY_HDRSIZE            offsetof(AnyQuery, items)</div><div>+#define ANYQUERY_ITEMS(q)            ((q)-&gt;items)</div><div>+#define ANYQUERY_STRING(q, item)    ((const char *) (q) + (q)-&gt;str_off + (item)-&gt;payload)</div><div>+#define ANYQUERY_MAXITEMS \</div><div>+    ((MaxAllocSize - ANYQUERY_HDRSIZE) / sizeof(AnyQueryItem))</div><div>+</div><div>+#define DatumGetAnyQueryP(X)        ((AnyQuery *) PG_DETOAST_DATUM(X))</div><div>+#define PG_GETARG_ANYQUERY_P(n)        DatumGetAnyQueryP(PG_GETARG_DATUM(n))</div><div>+</div><div>+/*****************************************************************************</div><div>+ *  GiST signature key</div><div>+ *</div><div>+ *  Each indexed array is summarized as a fixed-size bit vector ("signature").</div><div>+ *  Each element contributes a single bit, chosen as</div><div>+ *      hash(element) mod (siglen * 8).</div><div>+ *  Internal node keys are bitwise unions of their children; the ALLISTRUE</div><div>+ *  flag short-circuits storage when every bit would be set.</div><div>+ *****************************************************************************/</div><div>+</div><div>+#define ANYARRAY_SIGLEN_DEFAULT        (63 * 4)    /* 252 bytes -&gt; 2016 bits */</div><div>+#define ANYARRAY_SIGLEN_MAX            GISTMaxIndexKeySize</div><div>+#define ANYARRAY_ALLISTRUE            0x01</div><div>+</div><div>+typedef struct AnyArrayGistKey</div><div>+{<!-- --></div><div>+    int32        vl_len_;        /* varlena header (do not touch directly!) */</div><div>+    int32        flag;</div><div>+    char        data[FLEXIBLE_ARRAY_MEMBER];    /* bit vector, siglen bytes */</div><div>+} AnyArrayGistKey;</div><div>+</div><div>+typedef struct AnyArrayGistOptions</div><div>+{<!-- --></div><div>+    int32        vl_len_;</div><div>+    int            siglen;            /* signature length in bytes */</div><div>+} AnyArrayGistOptions;</div><div>+</div><div>+#define ANYARRAY_GET_SIGLEN()        (PG_HAS_OPCLASS_OPTIONS() ? \</div><div>+                                     ((AnyArrayGistOptions *) PG_GET_OPCLASS_OPTIONS())-&gt;siglen : \</div><div>+                                     ANYARRAY_SIGLEN_DEFAULT)</div><div>+</div><div>+#define ANYARRAY_GKEY_HDR            (VARHDRSZ + sizeof(int32))</div><div>+#define ANYARRAY_GKEY_SIZE(flag, siglen) \</div><div>+    (ANYARRAY_GKEY_HDR + (((flag) &amp; ANYARRAY_ALLISTRUE) ? 0 : (siglen)))</div><div>+#define ANYARRAY_GKEY_ISALLTRUE(k)    (((AnyArrayGistKey *) (k))-&gt;flag &amp; ANYARRAY_ALLISTRUE)</div><div>+#define ANYARRAY_GKEY_SIGN(k)        ((unsigned char *) (((AnyArrayGistKey *) (k))-&gt;data))</div><div>+</div><div>+/* GIN strategy numbers; the first four match core's gin/array_ops */</div><div>+#define ANYARRAY_GIN_OVERLAP_STRATEGY    1</div><div>+#define ANYARRAY_GIN_CONTAINS_STRATEGY    2</div><div>+#define ANYARRAY_GIN_CONTAINED_STRATEGY    3</div><div>+#define ANYARRAY_GIN_EQUAL_STRATEGY        4</div><div>+#define ANYARRAY_GIN_BOOLEAN_STRATEGY    5</div><div>+</div><div>+#define ANYARRAY_HASHVAL(h, siglen)    ((unsigned int) (h) % ((siglen) * BITS_PER_BYTE))</div><div>+#define ANYARRAY_SETBIT(sig, h, siglen) \</div><div>+    ((sig)[(ANYARRAY_HASHVAL((h), (siglen)) / BITS_PER_BYTE)] |= \</div><div>+        (1 &lt;&lt; (ANYARRAY_HASHVAL((h), (siglen)) % BITS_PER_BYTE)))</div><div>+#define ANYARRAY_GETBIT(sig, h, siglen) \</div><div>+    (((sig)[(ANYARRAY_HASHVAL((h), (siglen)) / BITS_PER_BYTE)] &gt;&gt; \</div><div>+        (ANYARRAY_HASHVAL((h), (siglen)) % BITS_PER_BYTE)) &amp; 1)</div><div>+</div><div>+#endif                            /* ANYARRAY_H */</div><div>diff --git a/contrib/anyarray/anyarray_bool.c b/contrib/anyarray/anyarray_bool.c</div><div>new file mode 100644</div><div>index 00000000000..325619837ef</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/anyarray_bool.c</div><div>@@ -0,0 +1,664 @@</div><div>+/*-------------------------------------------------------------------------</div><div>+ *</div><div>+ * anyarray_bool.c</div><div>+ *        Boolean query type for anyarray.</div><div>+ *</div><div>+ * Provides the anyquery type and the @@ operator.  A query value is a</div><div>+ * boolean expression whose leaves are text tokens; the element type is</div><div>+ * supplied at @@-time by the array operand, and the leaves are parsed</div><div>+ * through the element type's text input function.</div><div>+ *</div><div>+ * Grammar:</div><div>+ *</div><div>+ *      expr        ::= or_expr</div><div>+ *      or_expr     ::= and_expr ( '|' and_expr )*</div><div>+ *      and_expr    ::= unary_expr ( '&amp;' unary_expr )*</div><div>+ *      unary_expr  ::= '!' unary_expr | atom</div><div>+ *      atom        ::= VALUE | '(' expr ')'</div><div>+ *      VALUE       ::= bare_token | quoted_string</div><div>+ *      bare_token  ::= any run of characters that are not whitespace,</div><div>+ *                      '&amp;', '|', '!', '(', ')', or '"'</div><div>+ *      quoted_string := '"' chars-with-backslash-escapes '"'</div><div>+ *</div><div>+ *</div><div>+ * Copyright (c) 2026, PostgreSQL Global Development Group</div><div>+ *</div><div>+ * IDENTIFICATION</div><div>+ *        contrib/anyarray/anyarray_bool.c</div><div>+ *</div><div>+ *-------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+#include "postgres.h"</div><div>+</div><div>+#include "anyarray.h"</div><div>+</div><div>+#include "lib/stringinfo.h"</div><div>+#include "miscadmin.h"</div><div>+#include "nodes/miscnodes.h"</div><div>+#include "utils/array.h"</div><div>+#include "utils/builtins.h"</div><div>+#include "utils/lsyscache.h"</div><div>+#include "utils/memutils.h"</div><div>+</div><div>+PG_FUNCTION_INFO_V1(anyquery_in);</div><div>+PG_FUNCTION_INFO_V1(anyquery_out);</div><div>+PG_FUNCTION_INFO_V1(anyarray_boolop);</div><div>+PG_FUNCTION_INFO_V1(anyquery_boolop_rev);</div><div>+PG_FUNCTION_INFO_V1(anyquery_querytree);</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  Parser</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+/*</div><div>+ * Working state used while building the postfix item list.</div><div>+ *</div><div>+ * Items are emitted in postfix (reverse polish) order directly into the</div><div>+ * "items" array.  Value strings are concatenated into "strs"; each VAL item</div><div>+ * stores the byte offset of its string within strs-&gt;data.</div><div>+ */</div><div>+typedef struct ParserState</div><div>+{<!-- --></div><div>+    const char *cur;</div><div>+    const char *end;</div><div>+    AnyQueryItem *items;</div><div>+    int            nitems;</div><div>+    int            capitems;</div><div>+    StringInfoData strs;</div><div>+    struct Node *escontext;</div><div>+} ParserState;</div><div>+</div><div>+#define PARSE_FAIL(state, errc, ...) \</div><div>+    do { \</div><div>+        errsave((state)-&gt;escontext, \</div><div>+                (errcode(errc), \</div><div>+                 __VA_ARGS__)); \</div><div>+        return false; \</div><div>+    } while (0)</div><div>+</div><div>+static bool parse_expr(ParserState *state);</div><div>+</div><div>+static void</div><div>+skip_ws(ParserState *state)</div><div>+{<!-- --></div><div>+    while (state-&gt;cur &lt; state-&gt;end &amp;&amp;</div><div>+           (*state-&gt;cur == ' ' || *state-&gt;cur == '\t' ||</div><div>+            *state-&gt;cur == '\r' || *state-&gt;cur == '\n' ||</div><div>+            *state-&gt;cur == '\v' || *state-&gt;cur == '\f'))</div><div>+        state-&gt;cur++;</div><div>+}</div><div>+</div><div>+static bool</div><div>+is_value_char(char c)</div><div>+{<!-- --></div><div>+    if (c == '\0' || c == ' ' || c == '\t' || c == '\r' || c == '\n' ||</div><div>+        c == '\v' || c == '\f')</div><div>+        return false;</div><div>+    if (c == '&amp;' || c == '|' || c == '!' || c == '(' || c == ')' || c == '"')</div><div>+        return false;</div><div>+    return true;</div><div>+}</div><div>+</div><div>+static bool</div><div>+emit_item(ParserState *state, int16 type, int32 payload)</div><div>+{<!-- --></div><div>+    if (state-&gt;nitems == state-&gt;capitems)</div><div>+    {<!-- --></div><div>+        if ((size_t) state-&gt;capitems * 2 &gt; ANYQUERY_MAXITEMS)</div><div>+            PARSE_FAIL(state, ERRCODE_PROGRAM_LIMIT_EXCEEDED,</div><div>+                       errmsg("anyquery expression is too complex"));</div><div>+        state-&gt;capitems *= 2;</div><div>+        state-&gt;items = (AnyQueryItem *) repalloc(state-&gt;items,</div><div>+                                                 sizeof(AnyQueryItem) *</div><div>+                                                 state-&gt;capitems);</div><div>+    }</div><div>+    state-&gt;items[state-&gt;nitems].type = type;</div><div>+    state-&gt;items[state-&gt;nitems].left = 0;</div><div>+    state-&gt;items[state-&gt;nitems].payload = payload;</div><div>+    state-&gt;nitems++;</div><div>+    return true;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Consume a value token, append its string (with terminating NUL) to</div><div>+ * state-&gt;strs, and emit a VAL item whose payload is the byte offset of the</div><div>+ * string within strs-&gt;data.</div><div>+ */</div><div>+static bool</div><div>+parse_value(ParserState *state)</div><div>+{<!-- --></div><div>+    int32        off;</div><div>+</div><div>+    skip_ws(state);</div><div>+</div><div>+    if (state-&gt;cur &gt;= state-&gt;end)</div><div>+        PARSE_FAIL(state, ERRCODE_SYNTAX_ERROR,</div><div>+                   errmsg("unexpected end of input in anyquery"));</div><div>+</div><div>+    off = state-&gt;strs.len;</div><div>+</div><div>+    if (*state-&gt;cur == '"')</div><div>+    {<!-- --></div><div>+        state-&gt;cur++;            /* opening quote */</div><div>+        while (state-&gt;cur &lt; state-&gt;end &amp;&amp; *state-&gt;cur != '"')</div><div>+        {<!-- --></div><div>+            if (*state-&gt;cur == '\\' &amp;&amp; state-&gt;cur + 1 &lt; state-&gt;end)</div><div>+                state-&gt;cur++;</div><div>+            appendStringInfoChar(&amp;state-&gt;strs, *state-&gt;cur);</div><div>+            state-&gt;cur++;</div><div>+        }</div><div>+        if (state-&gt;cur &gt;= state-&gt;end)</div><div>+            PARSE_FAIL(state, ERRCODE_SYNTAX_ERROR,</div><div>+                       errmsg("unterminated quoted value in anyquery"));</div><div>+        state-&gt;cur++;            /* closing quote */</div><div>+    }</div><div>+    else if (is_value_char(*state-&gt;cur))</div><div>+    {<!-- --></div><div>+        while (state-&gt;cur &lt; state-&gt;end &amp;&amp; is_value_char(*state-&gt;cur))</div><div>+        {<!-- --></div><div>+            appendStringInfoChar(&amp;state-&gt;strs, *state-&gt;cur);</div><div>+            state-&gt;cur++;</div><div>+        }</div><div>+    }</div><div>+    else</div><div>+        PARSE_FAIL(state, ERRCODE_SYNTAX_ERROR,</div><div>+                   errmsg("expected value at character \"%c\"", *state-&gt;cur));</div><div>+</div><div>+    appendStringInfoChar(&amp;state-&gt;strs, '\0');</div><div>+    return emit_item(state, ANYQ_VAL, off);</div><div>+}</div><div>+</div><div>+static bool</div><div>+parse_atom(ParserState *state)</div><div>+{<!-- --></div><div>+    skip_ws(state);</div><div>+    if (state-&gt;cur &lt; state-&gt;end &amp;&amp; *state-&gt;cur == '(')</div><div>+    {<!-- --></div><div>+        state-&gt;cur++;</div><div>+        if (!parse_expr(state))</div><div>+            return false;</div><div>+        skip_ws(state);</div><div>+        if (state-&gt;cur &gt;= state-&gt;end || *state-&gt;cur != ')')</div><div>+            PARSE_FAIL(state, ERRCODE_SYNTAX_ERROR,</div><div>+                       errmsg("missing closing parenthesis in anyquery"));</div><div>+        state-&gt;cur++;</div><div>+        return true;</div><div>+    }</div><div>+    return parse_value(state);</div><div>+}</div><div>+</div><div>+static bool</div><div>+parse_not(ParserState *state)</div><div>+{<!-- --></div><div>+    skip_ws(state);</div><div>+    if (state-&gt;cur &lt; state-&gt;end &amp;&amp; *state-&gt;cur == '!')</div><div>+    {<!-- --></div><div>+        state-&gt;cur++;</div><div>+        if (!parse_not(state))</div><div>+            return false;</div><div>+        return emit_item(state, ANYQ_OPR, ANYQ_NOT);</div><div>+    }</div><div>+    return parse_atom(state);</div><div>+}</div><div>+</div><div>+static bool</div><div>+parse_and(ParserState *state)</div><div>+{<!-- --></div><div>+    if (!parse_not(state))</div><div>+        return false;</div><div>+    for (;;)</div><div>+    {<!-- --></div><div>+        skip_ws(state);</div><div>+        if (state-&gt;cur &gt;= state-&gt;end || *state-&gt;cur != '&amp;')</div><div>+            return true;</div><div>+        state-&gt;cur++;</div><div>+        if (!parse_not(state))</div><div>+            return false;</div><div>+        if (!emit_item(state, ANYQ_OPR, ANYQ_AND))</div><div>+            return false;</div><div>+    }</div><div>+}</div><div>+</div><div>+static bool</div><div>+parse_expr(ParserState *state)</div><div>+{<!-- --></div><div>+    check_stack_depth();</div><div>+</div><div>+    if (!parse_and(state))</div><div>+        return false;</div><div>+    for (;;)</div><div>+    {<!-- --></div><div>+        skip_ws(state);</div><div>+        if (state-&gt;cur &gt;= state-&gt;end || *state-&gt;cur != '|')</div><div>+            return true;</div><div>+        state-&gt;cur++;</div><div>+        if (!parse_and(state))</div><div>+            return false;</div><div>+        if (!emit_item(state, ANYQ_OPR, ANYQ_OR))</div><div>+            return false;</div><div>+    }</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Compute the "left" field for every OPR item by walking the postfix array</div><div>+ * top-down.  Returns false on overflow of the int16 left field.</div><div>+ */</div><div>+static bool</div><div>+compute_lefts(ParserState *state, int *pos)</div><div>+{<!-- --></div><div>+    int            mypos;</div><div>+</div><div>+    check_stack_depth();</div><div>+</div><div>+    mypos = (*pos)--;</div><div>+    Assert(mypos &gt;= 0);</div><div>+</div><div>+    if (state-&gt;items[mypos].type == ANYQ_VAL)</div><div>+    {<!-- --></div><div>+        state-&gt;items[mypos].left = 0;</div><div>+        return true;</div><div>+    }</div><div>+    else if (state-&gt;items[mypos].payload == ANYQ_NOT)</div><div>+    {<!-- --></div><div>+        state-&gt;items[mypos].left = -1;</div><div>+        return compute_lefts(state, pos);</div><div>+    }</div><div>+    else</div><div>+    {<!-- --></div><div>+        int            delta;</div><div>+</div><div>+        /* binary operator: walk right operand */</div><div>+        if (!compute_lefts(state, pos))</div><div>+            return false;</div><div>+        delta = *pos - mypos;</div><div>+        if (delta &lt; PG_INT16_MIN)</div><div>+        {<!-- --></div><div>+            errsave(state-&gt;escontext,</div><div>+                    (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),</div><div>+                     errmsg("anyquery expression is too complex")));</div><div>+            return false;</div><div>+        }</div><div>+        state-&gt;items[mypos].left = (int16) delta;</div><div>+        return compute_lefts(state, pos);</div><div>+    }</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  Input / output / debug</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+Datum</div><div>+anyquery_in(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    char       *buf = PG_GETARG_CSTRING(0);</div><div>+    ParserState state;</div><div>+    AnyQuery   *out;</div><div>+    Size        items_bytes;</div><div>+    Size        total;</div><div>+    int            pos;</div><div>+</div><div>+    state.cur = buf;</div><div>+    state.end = buf + strlen(buf);</div><div>+    state.capitems = 16;</div><div>+    state.items = (AnyQueryItem *) palloc(sizeof(AnyQueryItem) *</div><div>+                                          state.capitems);</div><div>+    state.nitems = 0;</div><div>+    initStringInfo(&amp;state.strs);</div><div>+    state.escontext = fcinfo-&gt;context;</div><div>+</div><div>+    if (!parse_expr(&amp;state))</div><div>+        PG_RETURN_NULL();</div><div>+</div><div>+    skip_ws(&amp;state);</div><div>+    if (state.cur &lt; state.end)</div><div>+        ereturn(state.escontext, (Datum) 0,</div><div>+                (errcode(ERRCODE_SYNTAX_ERROR),</div><div>+                 errmsg("unexpected trailing input in anyquery")));</div><div>+</div><div>+    if (state.nitems == 0)</div><div>+        ereturn(state.escontext, (Datum) 0,</div><div>+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),</div><div>+                 errmsg("empty anyquery")));</div><div>+</div><div>+    if ((size_t) state.nitems &gt; ANYQUERY_MAXITEMS)</div><div>+        ereturn(state.escontext, (Datum) 0,</div><div>+                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),</div><div>+                 errmsg("anyquery has too many items (%d, maximum %zu)",</div><div>+                        state.nitems, ANYQUERY_MAXITEMS)));</div><div>+</div><div>+    pos = state.nitems - 1;</div><div>+    if (!compute_lefts(&amp;state, &amp;pos))</div><div>+        PG_RETURN_NULL();</div><div>+    Assert(pos == -1);</div><div>+</div><div>+    items_bytes = MAXALIGN(state.nitems * sizeof(AnyQueryItem));</div><div>+    total = ANYQUERY_HDRSIZE + items_bytes + state.strs.len;</div><div>+</div><div>+    out = (AnyQuery *) palloc0(total);</div><div>+    SET_VARSIZE(out, total);</div><div>+    out-&gt;size = state.nitems;</div><div>+    out-&gt;str_off = ANYQUERY_HDRSIZE + items_bytes;</div><div>+    out-&gt;str_len = state.strs.len;</div><div>+    memcpy(out-&gt;items, state.items, state.nitems * sizeof(AnyQueryItem));</div><div>+    memcpy((char *) out + out-&gt;str_off, state.strs.data, state.strs.len);</div><div>+</div><div>+    pfree(state.items);</div><div>+    pfree(state.strs.data);</div><div>+</div><div>+    PG_RETURN_POINTER(out);</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Quote a value string if it contains anything the parser would consider</div><div>+ * special.  Otherwise emit it verbatim.</div><div>+ */</div><div>+static void</div><div>+append_value_token(StringInfo out, const char *s)</div><div>+{<!-- --></div><div>+    const char *p;</div><div>+    bool        need_quote = (*s == '\0');</div><div>+</div><div>+    for (p = s; *p; p++)</div><div>+    {<!-- --></div><div>+        if (!is_value_char(*p))</div><div>+        {<!-- --></div><div>+            need_quote = true;</div><div>+            break;</div><div>+        }</div><div>+    }</div><div>+</div><div>+    if (!need_quote)</div><div>+    {<!-- --></div><div>+        appendStringInfoString(out, s);</div><div>+        return;</div><div>+    }</div><div>+</div><div>+    appendStringInfoChar(out, '"');</div><div>+    for (p = s; *p; p++)</div><div>+    {<!-- --></div><div>+        if (*p == '"' || *p == '\\')</div><div>+            appendStringInfoChar(out, '\\');</div><div>+        appendStringInfoChar(out, *p);</div><div>+    }</div><div>+    appendStringInfoChar(out, '"');</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Infix walker for the output function.  Returns the postfix index that</div><div>+ * "cur" decremented to so the caller can chain.</div><div>+ */</div><div>+static int</div><div>+infix_walk(StringInfo out, AnyQuery *q, int cur, bool top)</div><div>+{<!-- --></div><div>+    AnyQueryItem *it;</div><div>+</div><div>+    check_stack_depth();</div><div>+    Assert(cur &gt;= 0);</div><div>+    it = &amp;q-&gt;items[cur];</div><div>+</div><div>+    if (it-&gt;type == ANYQ_VAL)</div><div>+    {<!-- --></div><div>+        append_value_token(out, ANYQUERY_STRING(q, it));</div><div>+        return cur - 1;</div><div>+    }</div><div>+    else if (it-&gt;payload == ANYQ_NOT)</div><div>+    {<!-- --></div><div>+        AnyQueryItem *child = &amp;q-&gt;items[cur - 1];</div><div>+        bool        paren = (child-&gt;type == ANYQ_OPR);</div><div>+</div><div>+        appendStringInfoChar(out, '!');</div><div>+        if (paren)</div><div>+            appendStringInfoString(out, "( ");</div><div>+        cur = infix_walk(out, q, cur - 1, false);</div><div>+        if (paren)</div><div>+            appendStringInfoString(out, " )");</div><div>+        return cur;</div><div>+    }</div><div>+    else</div><div>+    {<!-- --></div><div>+        int            op = it-&gt;payload;</div><div>+        StringInfoData right;</div><div>+        int            next;</div><div>+</div><div>+        if (op == ANYQ_OR &amp;&amp; !top)</div><div>+            appendStringInfoString(out, "( ");</div><div>+</div><div>+        /* right operand first into a side buffer */</div><div>+        initStringInfo(&amp;right);</div><div>+        next = infix_walk(&amp;right, q, cur - 1, false);</div><div>+        /* left operand into the main buffer */</div><div>+        cur = infix_walk(out, q, next, false);</div><div>+</div><div>+        appendStringInfo(out, " %c %s", op, right.data);</div><div>+        pfree(right.data);</div><div>+</div><div>+        if (op == ANYQ_OR &amp;&amp; !top)</div><div>+            appendStringInfoString(out, " )");</div><div>+        return cur;</div><div>+    }</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyquery_out(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    AnyQuery   *q = PG_GETARG_ANYQUERY_P(0);</div><div>+    StringInfoData out;</div><div>+</div><div>+    if (q-&gt;size &lt;= 0)</div><div>+        ereport(ERROR,</div><div>+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),</div><div>+                 errmsg("empty anyquery")));</div><div>+</div><div>+    initStringInfo(&amp;out);</div><div>+    (void) infix_walk(&amp;out, q, q-&gt;size - 1, true);</div><div>+</div><div>+    PG_RETURN_CSTRING(out.data);</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Debugging helper: produce a postfix string with operator codes spelled out.</div><div>+ * Mostly useful while writing tests.</div><div>+ */</div><div>+Datum</div><div>+anyquery_querytree(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    AnyQuery   *q = PG_GETARG_ANYQUERY_P(0);</div><div>+    StringInfoData out;</div><div>+    int            i;</div><div>+</div><div>+    initStringInfo(&amp;out);</div><div>+    for (i = 0; i &lt; q-&gt;size; i++)</div><div>+    {<!-- --></div><div>+        AnyQueryItem *it = &amp;q-&gt;items[i];</div><div>+</div><div>+        if (i &gt; 0)</div><div>+            appendStringInfoChar(&amp;out, ' ');</div><div>+        if (it-&gt;type == ANYQ_VAL)</div><div>+            append_value_token(&amp;out, ANYQUERY_STRING(q, it));</div><div>+        else</div><div>+            appendStringInfoChar(&amp;out, (char) it-&gt;payload);</div><div>+    }</div><div>+    PG_RETURN_TEXT_P(cstring_to_text_with_len(out.data, out.len));</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  Matching: anyarray @@ anyquery</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+typedef struct MatchContext</div><div>+{<!-- --></div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *sorted;            /* sorted array elements (owned) */</div><div>+    int            nelems;</div><div>+    /* cached parsed values for each VAL item in the query */</div><div>+    Datum       *parsed;</div><div>+    bool       *parsed_valid;</div><div>+} MatchContext;</div><div>+</div><div>+/*</div><div>+ * Look up val in the sorted element array via binary search.</div><div>+ */</div><div>+static bool</div><div>+contains_value(MatchContext *ctx, Datum val)</div><div>+{<!-- --></div><div>+    int            lo = 0;</div><div>+    int            hi = ctx-&gt;nelems;</div><div>+</div><div>+    while (lo &lt; hi)</div><div>+    {<!-- --></div><div>+        int            mid = lo + (hi - lo) / 2;</div><div>+        int            c = DatumGetInt32(FunctionCall2Coll(&amp;ctx-&gt;meta-&gt;cmp_proc,</div><div>+                                                        ctx-&gt;meta-&gt;typcollation,</div><div>+                                                        ctx-&gt;sorted[mid],</div><div>+                                                        val));</div><div>+</div><div>+        if (c == 0)</div><div>+            return true;</div><div>+        if (c &lt; 0)</div><div>+            lo = mid + 1;</div><div>+        else</div><div>+            hi = mid;</div><div>+    }</div><div>+    return false;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Evaluate one node of the postfix tree.  Recursive on operators.</div><div>+ */</div><div>+static bool</div><div>+eval_item(AnyQuery *q, int idx, MatchContext *ctx, Oid input_func,</div><div>+          int input_typioparam, int32 input_typmod)</div><div>+{<!-- --></div><div>+    AnyQueryItem *it;</div><div>+</div><div>+    check_stack_depth();</div><div>+    Assert(idx &gt;= 0);</div><div>+    it = &amp;q-&gt;items[idx];</div><div>+</div><div>+    if (it-&gt;type == ANYQ_VAL)</div><div>+    {<!-- --></div><div>+        Datum        v;</div><div>+</div><div>+        if (!ctx-&gt;parsed_valid[idx])</div><div>+        {<!-- --></div><div>+            const char *s = ANYQUERY_STRING(q, it);</div><div>+</div><div>+            v = OidInputFunctionCall(input_func, (char *) s,</div><div>+                                     input_typioparam, input_typmod);</div><div>+            ctx-&gt;parsed[idx] = v;</div><div>+            ctx-&gt;parsed_valid[idx] = true;</div><div>+        }</div><div>+        return contains_value(ctx, ctx-&gt;parsed[idx]);</div><div>+    }</div><div>+    else if (it-&gt;payload == ANYQ_NOT)</div><div>+    {<!-- --></div><div>+        return !eval_item(q, idx - 1, ctx, input_func,</div><div>+                          input_typioparam, input_typmod);</div><div>+    }</div><div>+    else if (it-&gt;payload == ANYQ_AND)</div><div>+    {<!-- --></div><div>+        if (!eval_item(q, idx + it-&gt;left, ctx, input_func,</div><div>+                       input_typioparam, input_typmod))</div><div>+            return false;</div><div>+        return eval_item(q, idx - 1, ctx, input_func,</div><div>+                         input_typioparam, input_typmod);</div><div>+    }</div><div>+    else                        /* ANYQ_OR */</div><div>+    {<!-- --></div><div>+        if (eval_item(q, idx + it-&gt;left, ctx, input_func,</div><div>+                      input_typioparam, input_typmod))</div><div>+            return true;</div><div>+        return eval_item(q, idx - 1, ctx, input_func,</div><div>+                         input_typioparam, input_typmod);</div><div>+    }</div><div>+}</div><div>+</div><div>+static Datum</div><div>+do_boolop(FunctionCallInfo fcinfo, ArrayType *arr, AnyQuery *q)</div><div>+{<!-- --></div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *values;</div><div>+    bool       *nulls;</div><div>+    int            nelems;</div><div>+    Oid            input_func;</div><div>+    Oid            input_typioparam;</div><div>+    bool        result;</div><div>+    MatchContext ctx;</div><div>+</div><div>+    ANYARRAY_CHECK_ARRAY(arr);</div><div>+</div><div>+    if (q-&gt;size &lt;= 0)</div><div>+        ereport(ERROR,</div><div>+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),</div><div>+                 errmsg("empty anyquery")));</div><div>+</div><div>+    meta = anyarray_get_meta(fcinfo, ARR_ELEMTYPE(arr), false);</div><div>+</div><div>+    if (ARR_NDIM(arr) == 0)</div><div>+    {<!-- --></div><div>+        nelems = 0;</div><div>+        values = NULL;</div><div>+    }</div><div>+    else</div><div>+    {<!-- --></div><div>+        deconstruct_array(arr, meta-&gt;element_type, meta-&gt;typlen,</div><div>+                          meta-&gt;typbyval, meta-&gt;typalign,</div><div>+                          &amp;values, &amp;nulls, &amp;nelems);</div><div>+        pfree(nulls);</div><div>+        if (nelems &gt; 1)</div><div>+            qsort_arg(values, nelems, sizeof(Datum),</div><div>+                      anyarray_cmp_datum, meta);</div><div>+    }</div><div>+</div><div>+    getTypeInputInfo(meta-&gt;element_type, &amp;input_func, &amp;input_typioparam);</div><div>+</div><div>+    ctx.meta = meta;</div><div>+    ctx.sorted = values;</div><div>+    ctx.nelems = nelems;</div><div>+    ctx.parsed = (Datum *) palloc0(sizeof(Datum) * q-&gt;size);</div><div>+    ctx.parsed_valid = (bool *) palloc0(sizeof(bool) * q-&gt;size);</div><div>+</div><div>+    result = eval_item(q, q-&gt;size - 1, &amp;ctx, input_func,</div><div>+                       input_typioparam, -1);</div><div>+</div><div>+    pfree(ctx.parsed);</div><div>+    pfree(ctx.parsed_valid);</div><div>+    if (values)</div><div>+        pfree(values);</div><div>+</div><div>+    PG_RETURN_BOOL(result);</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_boolop(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *arr = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    AnyQuery   *q = PG_GETARG_ANYQUERY_P(1);</div><div>+    Datum        result = do_boolop(fcinfo, arr, q);</div><div>+</div><div>+    PG_FREE_IF_COPY(arr, 0);</div><div>+    PG_FREE_IF_COPY(q, 1);</div><div>+    return result;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Commutator: anyquery ~~ anyarray simply swaps the arguments.</div><div>+ */</div><div>+Datum</div><div>+anyquery_boolop_rev(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    AnyQuery   *q = PG_GETARG_ANYQUERY_P(0);</div><div>+    ArrayType  *arr = PG_GETARG_ARRAYTYPE_P(1);</div><div>+    Datum        result = do_boolop(fcinfo, arr, q);</div><div>+</div><div>+    PG_FREE_IF_COPY(q, 0);</div><div>+    PG_FREE_IF_COPY(arr, 1);</div><div>+    return result;</div><div>+}</div><div>diff --git a/contrib/anyarray/anyarray_gin.c b/contrib/anyarray/anyarray_gin.c</div><div>new file mode 100644</div><div>index 00000000000..d9d201c12d5</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/anyarray_gin.c</div><div>@@ -0,0 +1,374 @@</div><div>+/*-------------------------------------------------------------------------</div><div>+ *</div><div>+ * anyarray_gin.c</div><div>+ *        GIN opclasses that add the @@ (anyarray, anyquery) boolean search</div><div>+ *        operator to the existing array_ops behaviour.</div><div>+ *</div><div>+ * GIN's extractQuery support function doesn't see fn_expr, so the element</div><div>+ * type cannot be derived dynamically when the query is an anyquery (which</div><div>+ * carries only text tokens).  We work around this by exposing one opclass</div><div>+ * per concrete element type; each is a thin C wrapper that dispatches on</div><div>+ * the strategy number, delegating standard operators to inline replicas of</div><div>+ * core's array_ops behaviour and parsing anyquery tokens via the type's</div><div>+ * input function for strategy 5.</div><div>+ *</div><div>+ * Copyright (c) 2026, PostgreSQL Global Development Group</div><div>+ *</div><div>+ * IDENTIFICATION</div><div>+ *        contrib/anyarray/anyarray_gin.c</div><div>+ *</div><div>+ *-------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+#include "postgres.h"</div><div>+</div><div>+#include "anyarray.h"</div><div>+</div><div>+#include "access/gin.h"</div><div>+#include "access/stratnum.h"</div><div>+#include "catalog/pg_type.h"</div><div>+#include "utils/array.h"</div><div>+#include "utils/builtins.h"</div><div>+#include "utils/lsyscache.h"</div><div>+</div><div>+PG_FUNCTION_INFO_V1(anyarray_gin_extract_query_int8);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gin_extract_query_uuid);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gin_extract_query_text);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gin_consistent_int8);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gin_consistent_uuid);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gin_consistent_text);</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  Common: extractQuery dispatcher</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+/*</div><div>+ * Extract array elements as GIN keys (used for strategies 1-4).</div><div>+ *</div><div>+ * Mirrors core's ginqueryarrayextract logic, but inlined so we don't depend</div><div>+ * on it being callable.  Returns a freshly-palloc'd Datum vector.</div><div>+ */</div><div>+static Datum *</div><div>+extract_array_keys(ArrayType *arr, AnyArrayTypeInfo *meta,</div><div>+                   int32 *nentries, bool **nulls_out)</div><div>+{<!-- --></div><div>+    Datum       *values;</div><div>+    bool       *nulls;</div><div>+    int            n;</div><div>+    int            i;</div><div>+    int            j = 0;</div><div>+</div><div>+    if (ARR_NDIM(arr) == 0)</div><div>+    {<!-- --></div><div>+        *nentries = 0;</div><div>+        *nulls_out = NULL;</div><div>+        return NULL;</div><div>+    }</div><div>+</div><div>+    deconstruct_array(arr, meta-&gt;element_type, meta-&gt;typlen,</div><div>+                      meta-&gt;typbyval, meta-&gt;typalign,</div><div>+                      &amp;values, &amp;nulls, &amp;n);</div><div>+</div><div>+    /* compact out NULL entries (we treat NULLs as never-present) */</div><div>+    for (i = 0; i &lt; n; i++)</div><div>+    {<!-- --></div><div>+        if (!nulls[i])</div><div>+        {<!-- --></div><div>+            if (j != i)</div><div>+            {<!-- --></div><div>+                values[j] = values[i];</div><div>+                nulls[j] = false;</div><div>+            }</div><div>+            j++;</div><div>+        }</div><div>+    }</div><div>+</div><div>+    *nentries = j;</div><div>+    *nulls_out = nulls;</div><div>+    return values;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Walk the anyquery: every VAL is a key that must be looked up in the GIN</div><div>+ * index.  Operators (&amp; | !) do not contribute keys.  Returns true if at</div><div>+ * least one VAL must be present for the query to possibly match (i.e. the</div><div>+ * query is not e.g. "!foo").</div><div>+ */</div><div>+static bool</div><div>+anyquery_has_required(AnyQuery *q, int idx)</div><div>+{<!-- --></div><div>+    AnyQueryItem *it;</div><div>+</div><div>+    if (idx &lt; 0)</div><div>+        return false;</div><div>+    it = &amp;q-&gt;items[idx];</div><div>+</div><div>+    if (it-&gt;type == ANYQ_VAL)</div><div>+        return true;</div><div>+    if (it-&gt;payload == ANYQ_NOT)</div><div>+        return false;            /* assume non-required under NOT */</div><div>+    if (it-&gt;payload == ANYQ_AND)</div><div>+        return anyquery_has_required(q, idx + it-&gt;left) ||</div><div>+            anyquery_has_required(q, idx - 1);</div><div>+    /* OR: both sides must contain required values */</div><div>+    return anyquery_has_required(q, idx + it-&gt;left) &amp;&amp;</div><div>+        anyquery_has_required(q, idx - 1);</div><div>+}</div><div>+</div><div>+static Datum</div><div>+do_gin_extract_query(FunctionCallInfo fcinfo, Oid elem_type)</div><div>+{<!-- --></div><div>+    Datum        queryDatum = PG_GETARG_DATUM(0);</div><div>+    int32       *nentries = (int32 *) PG_GETARG_POINTER(1);</div><div>+    StrategyNumber strat = PG_GETARG_UINT16(2);</div><div>+</div><div>+    /* PG_GETARG_POINTER(3): partial_matches -- not used */</div><div>+    /* PG_GETARG_POINTER(4): extra_data -- not used */</div><div>+    bool      **nullFlags = (bool **) PG_GETARG_POINTER(5);</div><div>+    int32       *searchMode = (int32 *) PG_GETARG_POINTER(6);</div><div>+    Datum       *keys = NULL;</div><div>+</div><div>+    *nentries = 0;</div><div>+    *nullFlags = NULL;</div><div>+    *searchMode = GIN_SEARCH_MODE_DEFAULT;</div><div>+</div><div>+    if (strat == ANYARRAY_GIN_BOOLEAN_STRATEGY)</div><div>+    {<!-- --></div><div>+        AnyQuery   *q = DatumGetAnyQueryP(queryDatum);</div><div>+        Oid            input_func;</div><div>+        Oid            input_typioparam;</div><div>+        int            i;</div><div>+        int            k = 0;</div><div>+</div><div>+        if (q-&gt;size &lt;= 0)</div><div>+            PG_RETURN_POINTER(NULL);</div><div>+</div><div>+        /*</div><div>+         * If the query has no required VAL (e.g. just "!foo"), we must scan</div><div>+         * the whole index because rows containing NONE of the queried values</div><div>+         * are valid matches.</div><div>+         */</div><div>+        if (!anyquery_has_required(q, q-&gt;size - 1))</div><div>+            *searchMode = GIN_SEARCH_MODE_ALL;</div><div>+</div><div>+        getTypeInputInfo(elem_type, &amp;input_func, &amp;input_typioparam);</div><div>+        keys = (Datum *) palloc(sizeof(Datum) * q-&gt;size);</div><div>+</div><div>+        for (i = 0; i &lt; q-&gt;size; i++)</div><div>+        {<!-- --></div><div>+            AnyQueryItem *it = &amp;q-&gt;items[i];</div><div>+</div><div>+            if (it-&gt;type != ANYQ_VAL)</div><div>+                continue;</div><div>+            keys[k++] = OidInputFunctionCall(input_func,</div><div>+                                             (char *) ANYQUERY_STRING(q, it),</div><div>+                                             input_typioparam, -1);</div><div>+        }</div><div>+        *nentries = k;</div><div>+        PG_RETURN_POINTER(keys);</div><div>+    }</div><div>+    else</div><div>+    {<!-- --></div><div>+        ArrayType  *arr = DatumGetArrayTypeP(queryDatum);</div><div>+        AnyArrayTypeInfo *meta = anyarray_get_meta(fcinfo, elem_type, false);</div><div>+        bool       *nulls;</div><div>+</div><div>+        ANYARRAY_CHECK_ARRAY(arr);</div><div>+        keys = extract_array_keys(arr, meta, nentries, &amp;nulls);</div><div>+</div><div>+        switch (strat)</div><div>+        {<!-- --></div><div>+            case ANYARRAY_GIN_OVERLAP_STRATEGY:</div><div>+                *searchMode = GIN_SEARCH_MODE_DEFAULT;</div><div>+                break;</div><div>+            case ANYARRAY_GIN_CONTAINED_STRATEGY:</div><div>+                *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;</div><div>+                break;</div><div>+            case ANYARRAY_GIN_EQUAL_STRATEGY:</div><div>+                *searchMode = (*nentries &gt; 0)</div><div>+                    ? GIN_SEARCH_MODE_DEFAULT</div><div>+                    : GIN_SEARCH_MODE_INCLUDE_EMPTY;</div><div>+                break;</div><div>+            case ANYARRAY_GIN_CONTAINS_STRATEGY:</div><div>+                *searchMode = (*nentries &gt; 0)</div><div>+                    ? GIN_SEARCH_MODE_DEFAULT</div><div>+                    : GIN_SEARCH_MODE_ALL;</div><div>+                break;</div><div>+            default:</div><div>+                elog(ERROR, "anyarray_gin: unknown strategy number: %d", strat);</div><div>+        }</div><div>+</div><div>+        (void) nulls;            /* swallowed by extract_array_keys */</div><div>+        PG_RETURN_POINTER(keys);</div><div>+    }</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  Common: consistent dispatcher</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+/*</div><div>+ * Evaluate an anyquery against an array of "key present" flags.  The i-th</div><div>+ * VAL in postfix order corresponds to check[i] (we mapped them in</div><div>+ * extractQuery, so the j-th VAL we emitted is check[j]).  We rebuild that</div><div>+ * VAL-only mapping here as we walk the postfix tree.</div><div>+ */</div><div>+typedef struct AnyQueryCheck</div><div>+{<!-- --></div><div>+    const bool *check;            /* GIN's present-key flags */</div><div>+    int            next;            /* next index in check[] */</div><div>+} AnyQueryCheck;</div><div>+</div><div>+static bool</div><div>+eval_with_check(AnyQuery *q, int idx, bool *vals)</div><div>+{<!-- --></div><div>+    AnyQueryItem *it = &amp;q-&gt;items[idx];</div><div>+</div><div>+    if (it-&gt;type == ANYQ_VAL)</div><div>+        return vals[idx];</div><div>+    if (it-&gt;payload == ANYQ_NOT)</div><div>+        return !eval_with_check(q, idx - 1, vals);</div><div>+    if (it-&gt;payload == ANYQ_AND)</div><div>+        return eval_with_check(q, idx + it-&gt;left, vals) &amp;&amp;</div><div>+            eval_with_check(q, idx - 1, vals);</div><div>+    /* OR */</div><div>+    return eval_with_check(q, idx + it-&gt;left, vals) ||</div><div>+        eval_with_check(q, idx - 1, vals);</div><div>+}</div><div>+</div><div>+static Datum</div><div>+do_gin_consistent(FunctionCallInfo fcinfo, Oid elem_type)</div><div>+{<!-- --></div><div>+    bool       *check = (bool *) PG_GETARG_POINTER(0);</div><div>+    StrategyNumber strat = PG_GETARG_UINT16(1);</div><div>+    Datum        queryDatum = PG_GETARG_DATUM(2);</div><div>+    int32        nkeys = PG_GETARG_INT32(3);</div><div>+</div><div>+    /* PG_GETARG_POINTER(4): extra_data -- not used */</div><div>+    bool       *recheck = (bool *) PG_GETARG_POINTER(5);</div><div>+    bool        result = false;</div><div>+    int            i;</div><div>+</div><div>+    (void) elem_type;</div><div>+</div><div>+    if (strat == ANYARRAY_GIN_BOOLEAN_STRATEGY)</div><div>+    {<!-- --></div><div>+        AnyQuery   *q = DatumGetAnyQueryP(queryDatum);</div><div>+        bool       *vals;</div><div>+        int            k = 0;</div><div>+</div><div>+        *recheck = false;</div><div>+</div><div>+        if (q-&gt;size &lt;= 0)</div><div>+            PG_RETURN_BOOL(false);</div><div>+</div><div>+        /* Map each VAL postfix slot to its position in check[]. */</div><div>+        vals = (bool *) palloc(sizeof(bool) * q-&gt;size);</div><div>+        for (i = 0; i &lt; q-&gt;size; i++)</div><div>+        {<!-- --></div><div>+            if (q-&gt;items[i].type == ANYQ_VAL)</div><div>+                vals[i] = check[k++];</div><div>+        }</div><div>+        result = eval_with_check(q, q-&gt;size - 1, vals);</div><div>+        pfree(vals);</div><div>+        PG_RETURN_BOOL(result);</div><div>+    }</div><div>+</div><div>+    switch (strat)</div><div>+    {<!-- --></div><div>+        case ANYARRAY_GIN_OVERLAP_STRATEGY:</div><div>+            *recheck = false;</div><div>+</div><div>+            /*</div><div>+             * GIN guarantees at least one true entry on entry; safe to say</div><div>+             * yes</div><div>+             */</div><div>+            for (i = 0; i &lt; nkeys; i++)</div><div>+            {<!-- --></div><div>+                if (check[i])</div><div>+                {<!-- --></div><div>+                    result = true;</div><div>+                    break;</div><div>+                }</div><div>+            }</div><div>+            break;</div><div>+        case ANYARRAY_GIN_CONTAINED_STRATEGY:</div><div>+            *recheck = true;</div><div>+            result = true;        /* must always recheck */</div><div>+            break;</div><div>+        case ANYARRAY_GIN_EQUAL_STRATEGY:</div><div>+            *recheck = true;</div><div>+            result = true;</div><div>+            for (i = 0; i &lt; nkeys; i++)</div><div>+            {<!-- --></div><div>+                if (!check[i])</div><div>+                {<!-- --></div><div>+                    result = false;</div><div>+                    break;</div><div>+                }</div><div>+            }</div><div>+            break;</div><div>+        case ANYARRAY_GIN_CONTAINS_STRATEGY:</div><div>+            *recheck = false;</div><div>+            result = true;</div><div>+            for (i = 0; i &lt; nkeys; i++)</div><div>+            {<!-- --></div><div>+                if (!check[i])</div><div>+                {<!-- --></div><div>+                    result = false;</div><div>+                    break;</div><div>+                }</div><div>+            }</div><div>+            break;</div><div>+        default:</div><div>+            elog(ERROR, "anyarray_gin: unknown strategy number: %d", strat);</div><div>+    }</div><div>+</div><div>+    PG_RETURN_BOOL(result);</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  Per-type wrappers</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+Datum</div><div>+anyarray_gin_extract_query_int8(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    return do_gin_extract_query(fcinfo, INT8OID);</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gin_extract_query_uuid(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    return do_gin_extract_query(fcinfo, UUIDOID);</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gin_extract_query_text(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    return do_gin_extract_query(fcinfo, TEXTOID);</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gin_consistent_int8(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    return do_gin_consistent(fcinfo, INT8OID);</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gin_consistent_uuid(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    return do_gin_consistent(fcinfo, UUIDOID);</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gin_consistent_text(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    return do_gin_consistent(fcinfo, TEXTOID);</div><div>+}</div><div>diff --git a/contrib/anyarray/anyarray_gist.c b/contrib/anyarray/anyarray_gist.c</div><div>new file mode 100644</div><div>index 00000000000..89570c63ded</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/anyarray_gist.c</div><div>@@ -0,0 +1,742 @@</div><div>+/*-------------------------------------------------------------------------</div><div>+ *</div><div>+ * anyarray_gist.c</div><div>+ *        Signature-based GiST opclass for anyarray.</div><div>+ *</div><div>+ * Each indexed array is summarised as a fixed-size bit vector; each element</div><div>+ * contributes a single bit chosen by its hash modulo the signature length.</div><div>+ * Internal node keys are bitwise unions of their children, with an</div><div>+ * ALLISTRUE short-circuit when every bit would be set.</div><div>+ *</div><div>+ * The opclass supports the array operators &amp;&amp;, @&gt;, &lt;@, = and the anyarray</div><div>+ * extension's @@ operator.  Because signatures are lossy, all matches are</div><div>+ * rechecked by GiST.</div><div>+ *</div><div>+ * Copyright (c) 2026, PostgreSQL Global Development Group</div><div>+ *</div><div>+ * IDENTIFICATION</div><div>+ *        contrib/anyarray/anyarray_gist.c</div><div>+ *</div><div>+ *-------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+#include "postgres.h"</div><div>+</div><div>+#include "anyarray.h"</div><div>+</div><div>+#include "access/gist.h"</div><div>+#include "access/reloptions.h"</div><div>+#include "access/stratnum.h"</div><div>+#include "catalog/pg_index.h"</div><div>+#include "catalog/pg_type.h"</div><div>+#include "miscadmin.h"</div><div>+#include "port/pg_bitutils.h"</div><div>+#include "utils/array.h"</div><div>+#include "utils/builtins.h"</div><div>+#include "utils/lsyscache.h"</div><div>+#include "utils/rel.h"</div><div>+</div><div>+/* Strategy numbers; share R-tree conventions where applicable. */</div><div>+#define ANYARRAY_OVERLAP_STRATEGY        3</div><div>+#define ANYARRAY_CONTAINS_STRATEGY        7</div><div>+#define ANYARRAY_CONTAINED_STRATEGY        8</div><div>+#define ANYARRAY_EQUAL_STRATEGY            18</div><div>+#define ANYARRAY_BOOLEAN_STRATEGY        20</div><div>+</div><div>+PG_FUNCTION_INFO_V1(anyarray_gist_key_in);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gist_key_out);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gist_consistent);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gist_compress);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gist_decompress);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gist_union);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gist_same);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gist_penalty);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gist_picksplit);</div><div>+PG_FUNCTION_INFO_V1(anyarray_gist_options);</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  Storage type stubs</div><div>+ *</div><div>+ *  The signature key is only ever constructed internally by the index AM,</div><div>+ *  so its text input/output functions reject all calls (mirroring</div><div>+ *  intbig_gkey in contrib/intarray).</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+Datum</div><div>+anyarray_gist_key_in(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ereport(ERROR,</div><div>+            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),</div><div>+             errmsg("cannot accept a value of type %s",</div><div>+                    "anyarray_gist_key")));</div><div>+    PG_RETURN_VOID();</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gist_key_out(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ereport(ERROR,</div><div>+            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),</div><div>+             errmsg("cannot display a value of type %s",</div><div>+                    "anyarray_gist_key")));</div><div>+    PG_RETURN_VOID();</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  Signature helpers</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+static AnyArrayGistKey *</div><div>+alloc_key(bool allistrue, int siglen, const unsigned char *src)</div><div>+{<!-- --></div><div>+    int32        flag = allistrue ? ANYARRAY_ALLISTRUE : 0;</div><div>+    Size        size = ANYARRAY_GKEY_SIZE(flag, siglen);</div><div>+    AnyArrayGistKey *k = (AnyArrayGistKey *) palloc(size);</div><div>+</div><div>+    SET_VARSIZE(k, size);</div><div>+    k-&gt;flag = flag;</div><div>+    if (!allistrue)</div><div>+    {<!-- --></div><div>+        if (src)</div><div>+            memcpy(k-&gt;data, src, siglen);</div><div>+        else</div><div>+            memset(k-&gt;data, 0, siglen);</div><div>+    }</div><div>+    return k;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Set the bit corresponding to "val" (a btree hash value) in "sig", a bit</div><div>+ * vector of length "siglen" bytes.</div><div>+ */</div><div>+static inline void</div><div>+set_bit(unsigned char *sig, uint32 hashval, int siglen)</div><div>+{<!-- --></div><div>+    uint32        bit = hashval % ((uint32) siglen * BITS_PER_BYTE);</div><div>+</div><div>+    sig[bit / BITS_PER_BYTE] |= (1U &lt;&lt; (bit % BITS_PER_BYTE));</div><div>+}</div><div>+</div><div>+static inline bool</div><div>+get_bit(const unsigned char *sig, uint32 hashval, int siglen)</div><div>+{<!-- --></div><div>+    uint32        bit = hashval % ((uint32) siglen * BITS_PER_BYTE);</div><div>+</div><div>+    return (sig[bit / BITS_PER_BYTE] &gt;&gt; (bit % BITS_PER_BYTE)) &amp; 1;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Hash a single Datum of the given element type, returning an unsigned 32-bit</div><div>+ * value suitable for indexing the bit vector.</div><div>+ */</div><div>+static uint32</div><div>+hash_elem(AnyArrayTypeInfo *meta, Datum value)</div><div>+{<!-- --></div><div>+    Datum        h = FunctionCall1Coll(&amp;meta-&gt;hash_proc, meta-&gt;typcollation,</div><div>+                                      value);</div><div>+</div><div>+    return (uint32) DatumGetInt32(h);</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Hash each element of "arr" into "sig".  The metadata must have a valid</div><div>+ * hash_proc (use anyarray_get_meta(..., true)).</div><div>+ */</div><div>+static void</div><div>+hash_array_into(unsigned char *sig, int siglen,</div><div>+                ArrayType *arr, AnyArrayTypeInfo *meta)</div><div>+{<!-- --></div><div>+    Datum       *values;</div><div>+    bool       *nulls;</div><div>+    int            nelems;</div><div>+    int            i;</div><div>+</div><div>+    if (ARR_NDIM(arr) == 0)</div><div>+        return;</div><div>+</div><div>+    deconstruct_array(arr, meta-&gt;element_type, meta-&gt;typlen,</div><div>+                      meta-&gt;typbyval, meta-&gt;typalign,</div><div>+                      &amp;values, &amp;nulls, &amp;nelems);</div><div>+</div><div>+    for (i = 0; i &lt; nelems; i++)</div><div>+    {<!-- --></div><div>+        if (!nulls[i])</div><div>+            set_bit(sig, hash_elem(meta, values[i]), siglen);</div><div>+    }</div><div>+</div><div>+    pfree(values);</div><div>+    pfree(nulls);</div><div>+}</div><div>+</div><div>+/* Count "1" bits in a buffer of "n" bytes. */</div><div>+static int</div><div>+popcount_bytes(const unsigned char *p, int n)</div><div>+{<!-- --></div><div>+    return pg_popcount((const char *) p, n);</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  compress / decompress</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+Datum</div><div>+anyarray_gist_compress(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);</div><div>+    GISTENTRY  *retval = entry;</div><div>+    int            siglen = ANYARRAY_GET_SIGLEN();</div><div>+</div><div>+    if (entry-&gt;leafkey)</div><div>+    {<!-- --></div><div>+        ArrayType  *arr = DatumGetArrayTypeP(entry-&gt;key);</div><div>+        AnyArrayGistKey *key;</div><div>+        AnyArrayTypeInfo *meta;</div><div>+</div><div>+        ANYARRAY_CHECK_ARRAY(arr);</div><div>+</div><div>+        meta = anyarray_get_meta(fcinfo, ARR_ELEMTYPE(arr), true);</div><div>+</div><div>+        key = alloc_key(false, siglen, NULL);</div><div>+        hash_array_into(ANYARRAY_GKEY_SIGN(key), siglen, arr, meta);</div><div>+</div><div>+        retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));</div><div>+        gistentryinit(*retval, PointerGetDatum(key), entry-&gt;rel, entry-&gt;page,</div><div>+                      entry-&gt;offset, false);</div><div>+    }</div><div>+    else if (!ANYARRAY_GKEY_ISALLTRUE(DatumGetPointer(entry-&gt;key)))</div><div>+    {<!-- --></div><div>+        AnyArrayGistKey *k = (AnyArrayGistKey *) DatumGetPointer(entry-&gt;key);</div><div>+</div><div>+        /*</div><div>+         * If every bit happens to be set, switch to ALLISTRUE storage so</div><div>+         * subsequent operations don't have to compare a full bit vector.</div><div>+         */</div><div>+        if (popcount_bytes(ANYARRAY_GKEY_SIGN(k), siglen) ==</div><div>+            siglen * BITS_PER_BYTE)</div><div>+        {<!-- --></div><div>+            AnyArrayGistKey *r = alloc_key(true, siglen, NULL);</div><div>+</div><div>+            retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));</div><div>+            gistentryinit(*retval, PointerGetDatum(r), entry-&gt;rel, entry-&gt;page,</div><div>+                          entry-&gt;offset, false);</div><div>+        }</div><div>+    }</div><div>+</div><div>+    PG_RETURN_POINTER(retval);</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gist_decompress(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    PG_RETURN_POINTER(PG_GETARG_POINTER(0));</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  union / same / penalty / picksplit / options</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+Datum</div><div>+anyarray_gist_union(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);</div><div>+    int           *sizep = (int *) PG_GETARG_POINTER(1);</div><div>+    int            siglen = ANYARRAY_GET_SIGLEN();</div><div>+    AnyArrayGistKey *out;</div><div>+    unsigned char *sig;</div><div>+    int            n = entryvec-&gt;n;</div><div>+    int            i;</div><div>+    bool        all = false;</div><div>+</div><div>+    out = alloc_key(false, siglen, NULL);</div><div>+    sig = ANYARRAY_GKEY_SIGN(out);</div><div>+</div><div>+    for (i = 0; i &lt; n; i++)</div><div>+    {<!-- --></div><div>+        AnyArrayGistKey *k = (AnyArrayGistKey *)</div><div>+            DatumGetPointer(entryvec-&gt;vector[i].key);</div><div>+</div><div>+        if (ANYARRAY_GKEY_ISALLTRUE(k))</div><div>+        {<!-- --></div><div>+            all = true;</div><div>+            break;</div><div>+        }</div><div>+        for (int j = 0; j &lt; siglen; j++)</div><div>+            sig[j] |= ANYARRAY_GKEY_SIGN(k)[j];</div><div>+    }</div><div>+</div><div>+    if (all || popcount_bytes(sig, siglen) == siglen * BITS_PER_BYTE)</div><div>+    {<!-- --></div><div>+        pfree(out);</div><div>+        out = alloc_key(true, siglen, NULL);</div><div>+    }</div><div>+</div><div>+    *sizep = VARSIZE(out);</div><div>+    PG_RETURN_POINTER(out);</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gist_same(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    AnyArrayGistKey *a = (AnyArrayGistKey *) PG_GETARG_POINTER(0);</div><div>+    AnyArrayGistKey *b = (AnyArrayGistKey *) PG_GETARG_POINTER(1);</div><div>+    bool       *result = (bool *) PG_GETARG_POINTER(2);</div><div>+    int            siglen = ANYARRAY_GET_SIGLEN();</div><div>+</div><div>+    if (ANYARRAY_GKEY_ISALLTRUE(a) || ANYARRAY_GKEY_ISALLTRUE(b))</div><div>+        *result = ANYARRAY_GKEY_ISALLTRUE(a) &amp;&amp; ANYARRAY_GKEY_ISALLTRUE(b);</div><div>+    else</div><div>+        *result = (memcmp(ANYARRAY_GKEY_SIGN(a), ANYARRAY_GKEY_SIGN(b),</div><div>+                          siglen) == 0);</div><div>+</div><div>+    PG_RETURN_POINTER(result);</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Hamming weight of (orig OR new) - Hamming weight of orig: the number of</div><div>+ * new bits a child would introduce.  ALLISTRUE entries have the maximum</div><div>+ * possible weight (siglen*8) so the answer is 0.</div><div>+ */</div><div>+Datum</div><div>+anyarray_gist_penalty(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);</div><div>+    GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);</div><div>+    float       *penalty = (float *) PG_GETARG_POINTER(2);</div><div>+    int            siglen = ANYARRAY_GET_SIGLEN();</div><div>+    AnyArrayGistKey *orig = (AnyArrayGistKey *) DatumGetPointer(origentry-&gt;key);</div><div>+    AnyArrayGistKey *new_ = (AnyArrayGistKey *) DatumGetPointer(newentry-&gt;key);</div><div>+</div><div>+    if (ANYARRAY_GKEY_ISALLTRUE(orig))</div><div>+    {<!-- --></div><div>+        *penalty = 0.0;</div><div>+    }</div><div>+    else if (ANYARRAY_GKEY_ISALLTRUE(new_))</div><div>+    {<!-- --></div><div>+        int            orig_bits = popcount_bytes(ANYARRAY_GKEY_SIGN(orig), siglen);</div><div>+</div><div>+        *penalty = (float) (siglen * BITS_PER_BYTE - orig_bits);</div><div>+    }</div><div>+    else</div><div>+    {<!-- --></div><div>+        unsigned char *o = ANYARRAY_GKEY_SIGN(orig);</div><div>+        unsigned char *n = ANYARRAY_GKEY_SIGN(new_);</div><div>+        int            added = 0;</div><div>+</div><div>+        for (int i = 0; i &lt; siglen; i++)</div><div>+        {<!-- --></div><div>+            unsigned char extra = n[i] &amp; ~o[i];</div><div>+</div><div>+            added += pg_number_of_ones[extra];</div><div>+        }</div><div>+        *penalty = (float) added;</div><div>+    }</div><div>+</div><div>+    PG_RETURN_POINTER(penalty);</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Standard signature picksplit: sort entries by Hamming weight and split</div><div>+ * down the middle.  This isn't optimal but is correct and balanced; the</div><div>+ * fancier Guttman-style splits used by intbig can be added later.</div><div>+ */</div><div>+typedef struct PickSplitEntry</div><div>+{<!-- --></div><div>+    OffsetNumber offset;</div><div>+    int            weight;</div><div>+} PickSplitEntry;</div><div>+</div><div>+static int</div><div>+psplit_cmp(const void *a, const void *b)</div><div>+{<!-- --></div><div>+    int            wa = ((const PickSplitEntry *) a)-&gt;weight;</div><div>+    int            wb = ((const PickSplitEntry *) b)-&gt;weight;</div><div>+</div><div>+    return (wa &gt; wb) - (wa &lt; wb);</div><div>+}</div><div>+</div><div>+static void</div><div>+or_into(unsigned char *dst, const unsigned char *src, int siglen)</div><div>+{<!-- --></div><div>+    for (int i = 0; i &lt; siglen; i++)</div><div>+        dst[i] |= src[i];</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gist_picksplit(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);</div><div>+    GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);</div><div>+    int            siglen = ANYARRAY_GET_SIGLEN();</div><div>+    int            n = entryvec-&gt;n - 1;    /* entries start at index 1 */</div><div>+    PickSplitEntry *items;</div><div>+    AnyArrayGistKey *left,</div><div>+               *right;</div><div>+    int            i;</div><div>+    int            split;</div><div>+</div><div>+    items = (PickSplitEntry *) palloc(n * sizeof(PickSplitEntry));</div><div>+    for (i = 0; i &lt; n; i++)</div><div>+    {<!-- --></div><div>+        AnyArrayGistKey *k = (AnyArrayGistKey *)</div><div>+            DatumGetPointer(entryvec-&gt;vector[i + 1].key);</div><div>+</div><div>+        items[i].offset = (OffsetNumber) (i + 1);</div><div>+        items[i].weight = ANYARRAY_GKEY_ISALLTRUE(k)</div><div>+            ? siglen * BITS_PER_BYTE</div><div>+            : popcount_bytes(ANYARRAY_GKEY_SIGN(k), siglen);</div><div>+    }</div><div>+    qsort(items, n, sizeof(PickSplitEntry), psplit_cmp);</div><div>+</div><div>+    v-&gt;spl_left = (OffsetNumber *) palloc(n * sizeof(OffsetNumber));</div><div>+    v-&gt;spl_right = (OffsetNumber *) palloc(n * sizeof(OffsetNumber));</div><div>+    v-&gt;spl_nleft = 0;</div><div>+    v-&gt;spl_nright = 0;</div><div>+    left = alloc_key(false, siglen, NULL);</div><div>+    right = alloc_key(false, siglen, NULL);</div><div>+</div><div>+    split = n / 2;</div><div>+    if (split == 0)</div><div>+        split = 1;</div><div>+    if (split == n)</div><div>+        split = n - 1;</div><div>+</div><div>+    for (i = 0; i &lt; split; i++)</div><div>+    {<!-- --></div><div>+        AnyArrayGistKey *k = (AnyArrayGistKey *)</div><div>+            DatumGetPointer(entryvec-&gt;vector[items[i].offset].key);</div><div>+</div><div>+        v-&gt;spl_left[v-&gt;spl_nleft++] = items[i].offset;</div><div>+        if (ANYARRAY_GKEY_ISALLTRUE(k))</div><div>+        {<!-- --></div><div>+            pfree(left);</div><div>+            left = alloc_key(true, siglen, NULL);</div><div>+        }</div><div>+        else if (!ANYARRAY_GKEY_ISALLTRUE(left))</div><div>+            or_into(ANYARRAY_GKEY_SIGN(left), ANYARRAY_GKEY_SIGN(k), siglen);</div><div>+    }</div><div>+    for (; i &lt; n; i++)</div><div>+    {<!-- --></div><div>+        AnyArrayGistKey *k = (AnyArrayGistKey *)</div><div>+            DatumGetPointer(entryvec-&gt;vector[items[i].offset].key);</div><div>+</div><div>+        v-&gt;spl_right[v-&gt;spl_nright++] = items[i].offset;</div><div>+        if (ANYARRAY_GKEY_ISALLTRUE(k))</div><div>+        {<!-- --></div><div>+            pfree(right);</div><div>+            right = alloc_key(true, siglen, NULL);</div><div>+        }</div><div>+        else if (!ANYARRAY_GKEY_ISALLTRUE(right))</div><div>+            or_into(ANYARRAY_GKEY_SIGN(right), ANYARRAY_GKEY_SIGN(k), siglen);</div><div>+    }</div><div>+</div><div>+    v-&gt;spl_ldatum = PointerGetDatum(left);</div><div>+    v-&gt;spl_rdatum = PointerGetDatum(right);</div><div>+</div><div>+    pfree(items);</div><div>+    PG_RETURN_POINTER(v);</div><div>+}</div><div>+</div><div>+/* opclass options: just the signature length, in bytes */</div><div>+static void</div><div>+fill_siglen_default(int *siglen)</div><div>+{<!-- --></div><div>+    *siglen = ANYARRAY_SIGLEN_DEFAULT;</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gist_options(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);</div><div>+</div><div>+    init_local_reloptions(relopts, sizeof(AnyArrayGistOptions));</div><div>+    add_local_int_reloption(relopts, "siglen",</div><div>+                            "signature length in bytes",</div><div>+                            ANYARRAY_SIGLEN_DEFAULT, 1, ANYARRAY_SIGLEN_MAX,</div><div>+                            offsetof(AnyArrayGistOptions, siglen));</div><div>+</div><div>+    (void) fill_siglen_default;</div><div>+    PG_RETURN_VOID();</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  consistent</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+/*</div><div>+ * For overlap: any single matching bit means the key MIGHT contain something</div><div>+ * the query references.  Returns true if at least one element of "query"</div><div>+ * hashes to a set bit in "sig"; ALLISTRUE keys trivially pass.</div><div>+ */</div><div>+static bool</div><div>+sig_overlap_array(const unsigned char *sig, int siglen,</div><div>+                  ArrayType *query, AnyArrayTypeInfo *meta)</div><div>+{<!-- --></div><div>+    Datum       *values;</div><div>+    bool       *nulls;</div><div>+    int            nelems;</div><div>+    int            i;</div><div>+    bool        found = false;</div><div>+</div><div>+    if (ARR_NDIM(query) == 0)</div><div>+        return false;</div><div>+</div><div>+    deconstruct_array(query, meta-&gt;element_type, meta-&gt;typlen,</div><div>+                      meta-&gt;typbyval, meta-&gt;typalign,</div><div>+                      &amp;values, &amp;nulls, &amp;nelems);</div><div>+</div><div>+    for (i = 0; i &lt; nelems; i++)</div><div>+    {<!-- --></div><div>+        if (nulls[i])</div><div>+            continue;</div><div>+        if (get_bit(sig, hash_elem(meta, values[i]), siglen))</div><div>+        {<!-- --></div><div>+            found = true;</div><div>+            break;</div><div>+        }</div><div>+    }</div><div>+    pfree(values);</div><div>+    pfree(nulls);</div><div>+    return found;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * For contains: every element of the query must hash to a set bit.</div><div>+ * If any required bit is missing the key cannot contain the query.</div><div>+ */</div><div>+static bool</div><div>+sig_contains_array(const unsigned char *sig, int siglen,</div><div>+                   ArrayType *query, AnyArrayTypeInfo *meta)</div><div>+{<!-- --></div><div>+    Datum       *values;</div><div>+    bool       *nulls;</div><div>+    int            nelems;</div><div>+    int            i;</div><div>+    bool        ok = true;</div><div>+</div><div>+    if (ARR_NDIM(query) == 0)</div><div>+        return true;</div><div>+</div><div>+    deconstruct_array(query, meta-&gt;element_type, meta-&gt;typlen,</div><div>+                      meta-&gt;typbyval, meta-&gt;typalign,</div><div>+                      &amp;values, &amp;nulls, &amp;nelems);</div><div>+</div><div>+    for (i = 0; i &lt; nelems; i++)</div><div>+    {<!-- --></div><div>+        if (nulls[i])</div><div>+            continue;</div><div>+        if (!get_bit(sig, hash_elem(meta, values[i]), siglen))</div><div>+        {<!-- --></div><div>+            ok = false;</div><div>+            break;</div><div>+        }</div><div>+    }</div><div>+    pfree(values);</div><div>+    pfree(nulls);</div><div>+    return ok;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Recursive postfix evaluator for sig_eval_query().</div><div>+ *</div><div>+ * "vals" holds the precomputed signature lookup for every VAL slot.  NOT</div><div>+ * is non-restrictive under a signature (the bit could still be set by some</div><div>+ * other inserted array), so we conservatively report true on negation; the</div><div>+ * GiST recheck step will then filter on the real values.</div><div>+ */</div><div>+static bool</div><div>+sig_eval_walk(AnyQuery *q, int idx, const bool *vals)</div><div>+{<!-- --></div><div>+    AnyQueryItem *it;</div><div>+</div><div>+    check_stack_depth();</div><div>+    Assert(idx &gt;= 0);</div><div>+    it = &amp;q-&gt;items[idx];</div><div>+</div><div>+    if (it-&gt;type == ANYQ_VAL)</div><div>+        return vals[idx];</div><div>+    if (it-&gt;payload == ANYQ_NOT)</div><div>+        return true;</div><div>+    if (it-&gt;payload == ANYQ_AND)</div><div>+        return sig_eval_walk(q, idx + it-&gt;left, vals) &amp;&amp;</div><div>+            sig_eval_walk(q, idx - 1, vals);</div><div>+    /* ANYQ_OR */</div><div>+    return sig_eval_walk(q, idx + it-&gt;left, vals) ||</div><div>+        sig_eval_walk(q, idx - 1, vals);</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Evaluate an anyquery against a signature.  Each VAL is parsed using the</div><div>+ * element type's text input, hashed, then looked up in the signature.</div><div>+ */</div><div>+static bool</div><div>+sig_eval_query(const unsigned char *sig, int siglen,</div><div>+               AnyQuery *q, AnyArrayTypeInfo *meta,</div><div>+               Oid input_func, Oid input_typioparam)</div><div>+{<!-- --></div><div>+    bool       *vals;</div><div>+    bool        result;</div><div>+    int            i;</div><div>+</div><div>+    if (q-&gt;size &lt;= 0)</div><div>+        return false;</div><div>+</div><div>+    vals = (bool *) palloc0(sizeof(bool) * q-&gt;size);</div><div>+</div><div>+    for (i = 0; i &lt; q-&gt;size; i++)</div><div>+    {<!-- --></div><div>+        AnyQueryItem *it = &amp;q-&gt;items[i];</div><div>+        Datum        v;</div><div>+</div><div>+        if (it-&gt;type != ANYQ_VAL)</div><div>+            continue;</div><div>+        v = OidInputFunctionCall(input_func,</div><div>+                                 (char *) ANYQUERY_STRING(q, it),</div><div>+                                 input_typioparam, -1);</div><div>+        vals[i] = get_bit(sig, hash_elem(meta, v), siglen);</div><div>+    }</div><div>+</div><div>+    result = sig_eval_walk(q, q-&gt;size - 1, vals);</div><div>+    pfree(vals);</div><div>+    return result;</div><div>+}</div><div>+</div><div>+Datum</div><div>+anyarray_gist_consistent(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);</div><div>+    StrategyNumber strat = (StrategyNumber) PG_GETARG_UINT16(2);</div><div>+    Oid            subtype = PG_GETARG_OID(3);</div><div>+    bool       *recheck = (bool *) PG_GETARG_POINTER(4);</div><div>+    AnyArrayGistKey *key = (AnyArrayGistKey *) DatumGetPointer(entry-&gt;key);</div><div>+    int            siglen = ANYARRAY_GET_SIGLEN();</div><div>+    bool        result;</div><div>+</div><div>+    /* All signature-based answers require a recheck. */</div><div>+    *recheck = true;</div><div>+</div><div>+    if (ANYARRAY_GKEY_ISALLTRUE(key))</div><div>+    {<!-- --></div><div>+        /* Cannot prune anything from an all-true signature. */</div><div>+        PG_RETURN_BOOL(true);</div><div>+    }</div><div>+</div><div>+    (void) subtype;</div><div>+</div><div>+    if (strat == ANYARRAY_BOOLEAN_STRATEGY)</div><div>+    {<!-- --></div><div>+        AnyQuery   *q = PG_GETARG_ANYQUERY_P(1);</div><div>+        AnyArrayTypeInfo *meta;</div><div>+        Form_pg_index ind;</div><div>+        Oid            coltype;</div><div>+        Oid            elemtype;</div><div>+        Oid            input_func;</div><div>+        Oid            input_typioparam;</div><div>+</div><div>+        /*</div><div>+         * The query is anyquery, so we recover the element type from the</div><div>+         * indexed column's pg_index entry.  The index's own rd_att gives the</div><div>+         * STORAGE type (anyarray_gist_key), not the original array type, so</div><div>+         * we read the indrelid + indkey instead.</div><div>+         */</div><div>+        ind = entry-&gt;rel-&gt;rd_index;</div><div>+        if (ind == NULL || ind-&gt;indnatts &lt; 1)</div><div>+            ereport(ERROR,</div><div>+                    (errcode(ERRCODE_INTERNAL_ERROR),</div><div>+                     errmsg("anyarray GiST index has no key column")));</div><div>+        coltype = get_atttype(ind-&gt;indrelid, ind-&gt;indkey.values[0]);</div><div>+        elemtype = get_element_type(coltype);</div><div>+        if (!OidIsValid(elemtype))</div><div>+            ereport(ERROR,</div><div>+                    (errcode(ERRCODE_DATATYPE_MISMATCH),</div><div>+                     errmsg("cannot determine element type for anyquery match")));</div><div>+</div><div>+        meta = anyarray_get_meta(fcinfo, elemtype, true);</div><div>+        getTypeInputInfo(elemtype, &amp;input_func, &amp;input_typioparam);</div><div>+</div><div>+        result = sig_eval_query(ANYARRAY_GKEY_SIGN(key), siglen, q, meta,</div><div>+                                input_func, input_typioparam);</div><div>+        PG_FREE_IF_COPY(q, 1);</div><div>+        PG_RETURN_BOOL(result);</div><div>+    }</div><div>+    else</div><div>+    {<!-- --></div><div>+        ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);</div><div>+        AnyArrayTypeInfo *meta;</div><div>+</div><div>+        ANYARRAY_CHECK_ARRAY(query);</div><div>+        meta = anyarray_get_meta(fcinfo, ARR_ELEMTYPE(query), true);</div><div>+</div><div>+        switch (strat)</div><div>+        {<!-- --></div><div>+            case ANYARRAY_OVERLAP_STRATEGY:</div><div>+                result = sig_overlap_array(ANYARRAY_GKEY_SIGN(key), siglen,</div><div>+                                           query, meta);</div><div>+                break;</div><div>+            case ANYARRAY_CONTAINS_STRATEGY:</div><div>+                result = sig_contains_array(ANYARRAY_GKEY_SIGN(key), siglen,</div><div>+                                            query, meta);</div><div>+                break;</div><div>+            case ANYARRAY_CONTAINED_STRATEGY:</div><div>+</div><div>+                /*</div><div>+                 * For "key &lt;@ query", signatures cannot exclude a non-leaf</div><div>+                 * key because its bits may come from many distinct children</div><div>+                 * which need not individually be contained in query. Always</div><div>+                 * recheck.</div><div>+                 */</div><div>+                if (GIST_LEAF(entry))</div><div>+                {<!-- --></div><div>+                    /*</div><div>+                     * At the leaf, every set bit must also have been set by</div><div>+                     * the query (i.e., element hashes to that bit).</div><div>+                     */</div><div>+                    unsigned char *qsig;</div><div>+                    int            i;</div><div>+</div><div>+                    qsig = (unsigned char *) palloc0(siglen);</div><div>+                    hash_array_into(qsig, siglen, query, meta);</div><div>+                    result = true;</div><div>+                    for (i = 0; i &lt; siglen; i++)</div><div>+                    {<!-- --></div><div>+                        if (ANYARRAY_GKEY_SIGN(key)[i] &amp; ~qsig[i])</div><div>+                        {<!-- --></div><div>+                            result = false;</div><div>+                            break;</div><div>+                        }</div><div>+                    }</div><div>+                    pfree(qsig);</div><div>+                }</div><div>+                else</div><div>+                    result = true;</div><div>+                break;</div><div>+            case ANYARRAY_EQUAL_STRATEGY:</div><div>+</div><div>+                /*</div><div>+                 * The leaf signature for an equal row contains every bit of</div><div>+                 * the query's hash; internal unions contain at least those</div><div>+                 * bits.  So "key contains query bits" is a sound</div><div>+                 * over-approximation that recheck will tighten.</div><div>+                 */</div><div>+                result = sig_contains_array(ANYARRAY_GKEY_SIGN(key), siglen,</div><div>+                                            query, meta);</div><div>+                break;</div><div>+            default:</div><div>+                elog(ERROR, "unrecognized strategy number: %d", strat);</div><div>+                result = false;</div><div>+                break;</div><div>+        }</div><div>+</div><div>+        PG_FREE_IF_COPY(query, 1);</div><div>+        PG_RETURN_BOOL(result);</div><div>+    }</div><div>+}</div><div>diff --git a/contrib/anyarray/anyarray_op.c b/contrib/anyarray/anyarray_op.c</div><div>new file mode 100644</div><div>index 00000000000..47a050bb0e3</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/anyarray_op.c</div><div>@@ -0,0 +1,613 @@</div><div>+/*-------------------------------------------------------------------------</div><div>+ *</div><div>+ * anyarray_op.c</div><div>+ *        Set-style operations on arrays of any type.</div><div>+ *</div><div>+ * All functions accept anyarray / anyelement inputs and dispatch to the</div><div>+ * element type's btree comparison function through anyarray_get_meta().</div><div>+ * The operations mirror the corresponding intarray operations but are</div><div>+ * type-polymorphic.</div><div>+ *</div><div>+ * Copyright (c) 2026, PostgreSQL Global Development Group</div><div>+ *</div><div>+ * IDENTIFICATION</div><div>+ *        contrib/anyarray/anyarray_op.c</div><div>+ *</div><div>+ *-------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+#include "postgres.h"</div><div>+</div><div>+#include "anyarray.h"</div><div>+</div><div>+#include "catalog/pg_type.h"</div><div>+#include "lib/qunique.h"</div><div>+#include "utils/array.h"</div><div>+#include "utils/builtins.h"</div><div>+#include "utils/lsyscache.h"</div><div>+</div><div>+</div><div>+/*</div><div>+ * deconstruct_arr_meta</div><div>+ *        Pull element values out of "arr" and fill in "*meta" for its type.</div><div>+ *</div><div>+ * Returns Datum array in *out_values (palloc'd), count in *out_nelems.  The</div><div>+ * input array must already have passed ANYARRAY_CHECK_ARRAY; we still gate</div><div>+ * on it defensively because all entry points feed through this helper.</div><div>+ */</div><div>+static void</div><div>+deconstruct_arr_meta(FunctionCallInfo fcinfo, ArrayType *arr,</div><div>+                     AnyArrayTypeInfo **out_meta,</div><div>+                     Datum **out_values, int *out_nelems)</div><div>+{<!-- --></div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *values;</div><div>+    bool       *nulls;</div><div>+    int            nelems;</div><div>+</div><div>+    ANYARRAY_CHECK_ARRAY(arr);</div><div>+</div><div>+    meta = anyarray_get_meta(fcinfo, ARR_ELEMTYPE(arr), false);</div><div>+</div><div>+    if (ARR_NDIM(arr) == 0)</div><div>+    {<!-- --></div><div>+        *out_meta = meta;</div><div>+        *out_values = NULL;</div><div>+        *out_nelems = 0;</div><div>+        return;</div><div>+    }</div><div>+</div><div>+    deconstruct_array(arr, meta-&gt;element_type,</div><div>+                      meta-&gt;typlen, meta-&gt;typbyval, meta-&gt;typalign,</div><div>+                      &amp;values, &amp;nulls, &amp;nelems);</div><div>+    pfree(nulls);                /* we've already rejected nulls above */</div><div>+</div><div>+    *out_meta = meta;</div><div>+    *out_values = values;</div><div>+    *out_nelems = nelems;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * make_array_from_datums</div><div>+ *        Build a 1-D array from the given Datum vector.  Empty -&gt; empty array.</div><div>+ */</div><div>+static ArrayType *</div><div>+make_array_from_datums(Datum *values, int nelems, AnyArrayTypeInfo *meta)</div><div>+{<!-- --></div><div>+    if (nelems == 0)</div><div>+        return construct_empty_array(meta-&gt;element_type);</div><div>+</div><div>+    return construct_array(values, nelems, meta-&gt;element_type,</div><div>+                           meta-&gt;typlen, meta-&gt;typbyval, meta-&gt;typalign);</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  sort / uniq</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+PG_FUNCTION_INFO_V1(anyarray_sort);</div><div>+PG_FUNCTION_INFO_V1(anyarray_sort_dir);</div><div>+PG_FUNCTION_INFO_V1(anyarray_uniq);</div><div>+</div><div>+/*</div><div>+ * Sort an array ascending.</div><div>+ */</div><div>+Datum</div><div>+anyarray_sort(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *arr = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *values;</div><div>+    int            nelems;</div><div>+</div><div>+    deconstruct_arr_meta(fcinfo, arr, &amp;meta, &amp;values, &amp;nelems);</div><div>+</div><div>+    if (nelems &gt; 1)</div><div>+        qsort_arg(values, nelems, sizeof(Datum), anyarray_cmp_datum, meta);</div><div>+</div><div>+    PG_RETURN_ARRAYTYPE_P(make_array_from_datums(values, nelems, meta));</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Sort an array.  The direction text is case-insensitive and must be one of</div><div>+ * 'asc' / 'ascending' or 'desc' / 'descending'.</div><div>+ */</div><div>+Datum</div><div>+anyarray_sort_dir(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *arr = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    text       *dir = PG_GETARG_TEXT_PP(1);</div><div>+    const char *dirstr = VARDATA_ANY(dir);</div><div>+    int            dirlen = VARSIZE_ANY_EXHDR(dir);</div><div>+    bool        ascending;</div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *values;</div><div>+    int            nelems;</div><div>+</div><div>+    if ((dirlen == 3 &amp;&amp; pg_strncasecmp(dirstr, "asc", 3) == 0) ||</div><div>+        (dirlen == 9 &amp;&amp; pg_strncasecmp(dirstr, "ascending", 9) == 0))</div><div>+        ascending = true;</div><div>+    else if ((dirlen == 4 &amp;&amp; pg_strncasecmp(dirstr, "desc", 4) == 0) ||</div><div>+             (dirlen == 10 &amp;&amp; pg_strncasecmp(dirstr, "descending", 10) == 0))</div><div>+        ascending = false;</div><div>+    else</div><div>+        ereport(ERROR,</div><div>+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),</div><div>+                 errmsg("second parameter must be \"asc\" or \"desc\"")));</div><div>+</div><div>+    deconstruct_arr_meta(fcinfo, arr, &amp;meta, &amp;values, &amp;nelems);</div><div>+</div><div>+    if (nelems &gt; 1)</div><div>+    {<!-- --></div><div>+        qsort_arg(values, nelems, sizeof(Datum), anyarray_cmp_datum, meta);</div><div>+        if (!ascending)</div><div>+        {<!-- --></div><div>+            Datum       *l = values;</div><div>+            Datum       *r = values + nelems - 1;</div><div>+</div><div>+            while (l &lt; r)</div><div>+            {<!-- --></div><div>+                Datum        tmp = *l;</div><div>+</div><div>+                *l++ = *r;</div><div>+                *r-- = tmp;</div><div>+            }</div><div>+        }</div><div>+    }</div><div>+</div><div>+    PG_RETURN_ARRAYTYPE_P(make_array_from_datums(values, nelems, meta));</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Remove duplicate elements; does not require pre-sorted input.</div><div>+ */</div><div>+Datum</div><div>+anyarray_uniq(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *arr = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *values;</div><div>+    int            nelems;</div><div>+    int            nunique;</div><div>+</div><div>+    deconstruct_arr_meta(fcinfo, arr, &amp;meta, &amp;values, &amp;nelems);</div><div>+</div><div>+    if (nelems &gt; 1)</div><div>+    {<!-- --></div><div>+        qsort_arg(values, nelems, sizeof(Datum), anyarray_cmp_datum, meta);</div><div>+        nunique = qunique_arg(values, nelems, sizeof(Datum),</div><div>+                              anyarray_cmp_datum, meta);</div><div>+    }</div><div>+    else</div><div>+        nunique = nelems;</div><div>+</div><div>+    PG_RETURN_ARRAYTYPE_P(make_array_from_datums(values, nunique, meta));</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  idx / subarray / cardinality</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+PG_FUNCTION_INFO_V1(anyarray_idx);</div><div>+PG_FUNCTION_INFO_V1(anyarray_subarray);</div><div>+PG_FUNCTION_INFO_V1(anyarray_subarray_to_end);</div><div>+PG_FUNCTION_INFO_V1(anyarray_icount);</div><div>+</div><div>+/*</div><div>+ * Return the 1-based index of the first occurrence of "elem" in "arr",</div><div>+ * or 0 if not found.</div><div>+ */</div><div>+Datum</div><div>+anyarray_idx(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *arr = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    Datum        elem = PG_GETARG_DATUM(1);</div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *values;</div><div>+    int            nelems;</div><div>+    int            i;</div><div>+</div><div>+    deconstruct_arr_meta(fcinfo, arr, &amp;meta, &amp;values, &amp;nelems);</div><div>+</div><div>+    for (i = 0; i &lt; nelems; i++)</div><div>+    {<!-- --></div><div>+        Datum        eq = FunctionCall2Coll(&amp;meta-&gt;eq_proc, meta-&gt;typcollation,</div><div>+                                           values[i], elem);</div><div>+</div><div>+        if (DatumGetBool(eq))</div><div>+            PG_RETURN_INT32(i + 1);</div><div>+    }</div><div>+</div><div>+    PG_RETURN_INT32(0);</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Common subarray extraction.  "start" is 1-based; if it is non-positive</div><div>+ * the function returns an empty array.  "len" gives the maximum number of</div><div>+ * elements to copy; if "have_len" is false the whole tail is returned.</div><div>+ */</div><div>+static ArrayType *</div><div>+do_subarray(FunctionCallInfo fcinfo, ArrayType *arr,</div><div>+            int32 start, int32 len, bool have_len)</div><div>+{<!-- --></div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *values;</div><div>+    int            nelems;</div><div>+    int            off;</div><div>+    int            n;</div><div>+</div><div>+    deconstruct_arr_meta(fcinfo, arr, &amp;meta, &amp;values, &amp;nelems);</div><div>+</div><div>+    if (start &lt; 1 || start &gt; nelems)</div><div>+        return construct_empty_array(meta-&gt;element_type);</div><div>+</div><div>+    off = start - 1;</div><div>+    if (have_len)</div><div>+    {<!-- --></div><div>+        if (len &lt;= 0)</div><div>+            return construct_empty_array(meta-&gt;element_type);</div><div>+        n = Min(len, nelems - off);</div><div>+    }</div><div>+    else</div><div>+        n = nelems - off;</div><div>+</div><div>+    return make_array_from_datums(values + off, n, meta);</div><div>+}</div><div>+</div><div>+/*</div><div>+ * subarray(arr, start, len)</div><div>+ */</div><div>+Datum</div><div>+anyarray_subarray(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *arr = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    int32        start = PG_GETARG_INT32(1);</div><div>+    int32        len = PG_GETARG_INT32(2);</div><div>+</div><div>+    PG_RETURN_ARRAYTYPE_P(do_subarray(fcinfo, arr, start, len, true));</div><div>+}</div><div>+</div><div>+/*</div><div>+ * subarray(arr, start) -- to end</div><div>+ */</div><div>+Datum</div><div>+anyarray_subarray_to_end(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *arr = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    int32        start = PG_GETARG_INT32(1);</div><div>+</div><div>+    PG_RETURN_ARRAYTYPE_P(do_subarray(fcinfo, arr, start, 0, false));</div><div>+}</div><div>+</div><div>+/*</div><div>+ * icount: total element count, intarray-style.  This duplicates</div><div>+ * built-in cardinality() but we expose it under the # operator the way</div><div>+ * intarray does.</div><div>+ */</div><div>+Datum</div><div>+anyarray_icount(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *arr = PG_GETARG_ARRAYTYPE_P(0);</div><div>+</div><div>+    ANYARRAY_CHECK_ARRAY(arr);</div><div>+    PG_RETURN_INT32(ANYARRAY_NELEMS(arr));</div><div>+}</div><div>+</div><div>+</div><div>+/* ------------------------------------------------------------------------</div><div>+ *  intersect / union / difference</div><div>+ * ------------------------------------------------------------------------</div><div>+ */</div><div>+</div><div>+PG_FUNCTION_INFO_V1(anyarray_intersect);</div><div>+PG_FUNCTION_INFO_V1(anyarray_union);</div><div>+PG_FUNCTION_INFO_V1(anyarray_union_elem);</div><div>+PG_FUNCTION_INFO_V1(anyarray_difference);</div><div>+PG_FUNCTION_INFO_V1(anyarray_difference_elem);</div><div>+</div><div>+/*</div><div>+ * deconstruct_two</div><div>+ *        Like deconstruct_arr_meta() but for two array inputs that must share</div><div>+ *        an element type.  The second array is allowed to be a different</div><div>+ *        ARRAY OID (it might come from another column) as long as the element</div><div>+ *        types match.</div><div>+ */</div><div>+static AnyArrayTypeInfo *</div><div>+deconstruct_two(FunctionCallInfo fcinfo,</div><div>+                ArrayType *a, ArrayType *b,</div><div>+                Datum **avals, int *anelems,</div><div>+                Datum **bvals, int *bnelems)</div><div>+{<!-- --></div><div>+    AnyArrayTypeInfo *meta;</div><div>+    bool       *nulls;</div><div>+</div><div>+    ANYARRAY_CHECK_ARRAY(a);</div><div>+    ANYARRAY_CHECK_ARRAY(b);</div><div>+</div><div>+    if (ARR_ELEMTYPE(a) != ARR_ELEMTYPE(b))</div><div>+        ereport(ERROR,</div><div>+                (errcode(ERRCODE_DATATYPE_MISMATCH),</div><div>+                 errmsg("cannot operate on arrays of different element types"),</div><div>+                 errdetail("Left operand has element type %s, right operand has %s.",</div><div>+                           format_type_be(ARR_ELEMTYPE(a)),</div><div>+                           format_type_be(ARR_ELEMTYPE(b)))));</div><div>+</div><div>+    meta = anyarray_get_meta(fcinfo, ARR_ELEMTYPE(a), false);</div><div>+</div><div>+    if (ARR_NDIM(a) == 0)</div><div>+    {<!-- --></div><div>+        *avals = NULL;</div><div>+        *anelems = 0;</div><div>+    }</div><div>+    else</div><div>+    {<!-- --></div><div>+        deconstruct_array(a, meta-&gt;element_type, meta-&gt;typlen,</div><div>+                          meta-&gt;typbyval, meta-&gt;typalign,</div><div>+                          avals, &amp;nulls, anelems);</div><div>+        pfree(nulls);</div><div>+    }</div><div>+</div><div>+    if (ARR_NDIM(b) == 0)</div><div>+    {<!-- --></div><div>+        *bvals = NULL;</div><div>+        *bnelems = 0;</div><div>+    }</div><div>+    else</div><div>+    {<!-- --></div><div>+        deconstruct_array(b, meta-&gt;element_type, meta-&gt;typlen,</div><div>+                          meta-&gt;typbyval, meta-&gt;typalign,</div><div>+                          bvals, &amp;nulls, bnelems);</div><div>+        pfree(nulls);</div><div>+    }</div><div>+</div><div>+    return meta;</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Sort + de-dup in place.  Returns the new length.</div><div>+ */</div><div>+static int</div><div>+sort_uniq(Datum *values, int nelems, AnyArrayTypeInfo *meta)</div><div>+{<!-- --></div><div>+    if (nelems &lt;= 1)</div><div>+        return nelems;</div><div>+</div><div>+    qsort_arg(values, nelems, sizeof(Datum), anyarray_cmp_datum, meta);</div><div>+    return qunique_arg(values, nelems, sizeof(Datum),</div><div>+                       anyarray_cmp_datum, meta);</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Intersection: returns the sorted, deduplicated values present in both</div><div>+ * inputs.</div><div>+ */</div><div>+Datum</div><div>+anyarray_intersect(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *a = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    ArrayType  *b = PG_GETARG_ARRAYTYPE_P(1);</div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *av,</div><div>+               *bv,</div><div>+               *out;</div><div>+    int            an,</div><div>+                bn,</div><div>+                i = 0,</div><div>+                j = 0,</div><div>+                k = 0;</div><div>+</div><div>+    meta = deconstruct_two(fcinfo, a, b, &amp;av, &amp;an, &amp;bv, &amp;bn);</div><div>+</div><div>+    an = sort_uniq(av, an, meta);</div><div>+    bn = sort_uniq(bv, bn, meta);</div><div>+</div><div>+    out = (Datum *) palloc(sizeof(Datum) * Min(an, bn));</div><div>+    while (i &lt; an &amp;&amp; j &lt; bn)</div><div>+    {<!-- --></div><div>+        int            cmp = anyarray_cmp_datum(&amp;av[i], &amp;bv[j], meta);</div><div>+</div><div>+        if (cmp == 0)</div><div>+        {<!-- --></div><div>+            out[k++] = av[i];</div><div>+            i++;</div><div>+            j++;</div><div>+        }</div><div>+        else if (cmp &lt; 0)</div><div>+            i++;</div><div>+        else</div><div>+            j++;</div><div>+    }</div><div>+</div><div>+    PG_RETURN_ARRAYTYPE_P(make_array_from_datums(out, k, meta));</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Union: returns the sorted, deduplicated values present in either input.</div><div>+ */</div><div>+Datum</div><div>+anyarray_union(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *a = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    ArrayType  *b = PG_GETARG_ARRAYTYPE_P(1);</div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *av,</div><div>+               *bv,</div><div>+               *out;</div><div>+    int            an,</div><div>+                bn,</div><div>+                i = 0,</div><div>+                j = 0,</div><div>+                k = 0;</div><div>+</div><div>+    meta = deconstruct_two(fcinfo, a, b, &amp;av, &amp;an, &amp;bv, &amp;bn);</div><div>+</div><div>+    an = sort_uniq(av, an, meta);</div><div>+    bn = sort_uniq(bv, bn, meta);</div><div>+</div><div>+    out = (Datum *) palloc(sizeof(Datum) * (an + bn));</div><div>+    while (i &lt; an &amp;&amp; j &lt; bn)</div><div>+    {<!-- --></div><div>+        int            cmp = anyarray_cmp_datum(&amp;av[i], &amp;bv[j], meta);</div><div>+</div><div>+        if (cmp == 0)</div><div>+        {<!-- --></div><div>+            out[k++] = av[i];</div><div>+            i++;</div><div>+            j++;</div><div>+        }</div><div>+        else if (cmp &lt; 0)</div><div>+            out[k++] = av[i++];</div><div>+        else</div><div>+            out[k++] = bv[j++];</div><div>+    }</div><div>+    while (i &lt; an)</div><div>+        out[k++] = av[i++];</div><div>+    while (j &lt; bn)</div><div>+        out[k++] = bv[j++];</div><div>+</div><div>+    PG_RETURN_ARRAYTYPE_P(make_array_from_datums(out, k, meta));</div><div>+}</div><div>+</div><div>+/*</div><div>+ * array | element : add element if not already present, returning the</div><div>+ * sorted, deduplicated result.</div><div>+ */</div><div>+Datum</div><div>+anyarray_union_elem(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *a = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    Datum        elem = PG_GETARG_DATUM(1);</div><div>+    Oid            elem_type = get_fn_expr_argtype(fcinfo-&gt;flinfo, 1);</div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *values;</div><div>+    int            nelems;</div><div>+    int            out_n;</div><div>+</div><div>+    ANYARRAY_CHECK_ARRAY(a);</div><div>+</div><div>+    if (ARR_ELEMTYPE(a) != elem_type)</div><div>+        ereport(ERROR,</div><div>+                (errcode(ERRCODE_DATATYPE_MISMATCH),</div><div>+                 errmsg("element type does not match array element type")));</div><div>+</div><div>+    meta = anyarray_get_meta(fcinfo, ARR_ELEMTYPE(a), false);</div><div>+</div><div>+    if (ARR_NDIM(a) == 0)</div><div>+    {<!-- --></div><div>+        nelems = 0;</div><div>+        values = (Datum *) palloc(sizeof(Datum));</div><div>+    }</div><div>+    else</div><div>+    {<!-- --></div><div>+        bool       *nulls;</div><div>+</div><div>+        deconstruct_array(a, meta-&gt;element_type, meta-&gt;typlen,</div><div>+                          meta-&gt;typbyval, meta-&gt;typalign,</div><div>+                          &amp;values, &amp;nulls, &amp;nelems);</div><div>+        pfree(nulls);</div><div>+        values = repalloc(values, sizeof(Datum) * (nelems + 1));</div><div>+    }</div><div>+</div><div>+    values[nelems++] = elem;</div><div>+</div><div>+    out_n = sort_uniq(values, nelems, meta);</div><div>+    PG_RETURN_ARRAYTYPE_P(make_array_from_datums(values, out_n, meta));</div><div>+}</div><div>+</div><div>+/*</div><div>+ * Difference: returns sorted, deduplicated values from "a" that are not in</div><div>+ * "b".</div><div>+ */</div><div>+Datum</div><div>+anyarray_difference(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *a = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    ArrayType  *b = PG_GETARG_ARRAYTYPE_P(1);</div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *av,</div><div>+               *bv,</div><div>+               *out;</div><div>+    int            an,</div><div>+                bn,</div><div>+                i = 0,</div><div>+                j = 0,</div><div>+                k = 0;</div><div>+</div><div>+    meta = deconstruct_two(fcinfo, a, b, &amp;av, &amp;an, &amp;bv, &amp;bn);</div><div>+</div><div>+    an = sort_uniq(av, an, meta);</div><div>+    bn = sort_uniq(bv, bn, meta);</div><div>+</div><div>+    out = (Datum *) palloc(sizeof(Datum) * an);</div><div>+    while (i &lt; an &amp;&amp; j &lt; bn)</div><div>+    {<!-- --></div><div>+        int            cmp = anyarray_cmp_datum(&amp;av[i], &amp;bv[j], meta);</div><div>+</div><div>+        if (cmp == 0)</div><div>+        {<!-- --></div><div>+            i++;</div><div>+            j++;</div><div>+        }</div><div>+        else if (cmp &lt; 0)</div><div>+            out[k++] = av[i++];</div><div>+        else</div><div>+            j++;</div><div>+    }</div><div>+    while (i &lt; an)</div><div>+        out[k++] = av[i++];</div><div>+</div><div>+    PG_RETURN_ARRAYTYPE_P(make_array_from_datums(out, k, meta));</div><div>+}</div><div>+</div><div>+/*</div><div>+ * array - element : remove all occurrences of "elem", preserving order.</div><div>+ * (Note: intarray returns the input order-preserved; we do the same.)</div><div>+ */</div><div>+Datum</div><div>+anyarray_difference_elem(PG_FUNCTION_ARGS)</div><div>+{<!-- --></div><div>+    ArrayType  *a = PG_GETARG_ARRAYTYPE_P(0);</div><div>+    Datum        elem = PG_GETARG_DATUM(1);</div><div>+    Oid            elem_type = get_fn_expr_argtype(fcinfo-&gt;flinfo, 1);</div><div>+    AnyArrayTypeInfo *meta;</div><div>+    Datum       *values;</div><div>+    int            nelems;</div><div>+    int            i,</div><div>+                k = 0;</div><div>+</div><div>+    ANYARRAY_CHECK_ARRAY(a);</div><div>+</div><div>+    if (ARR_ELEMTYPE(a) != elem_type)</div><div>+        ereport(ERROR,</div><div>+                (errcode(ERRCODE_DATATYPE_MISMATCH),</div><div>+                 errmsg("element type does not match array element type")));</div><div>+</div><div>+    meta = anyarray_get_meta(fcinfo, ARR_ELEMTYPE(a), false);</div><div>+</div><div>+    if (ARR_NDIM(a) == 0)</div><div>+        PG_RETURN_ARRAYTYPE_P(construct_empty_array(meta-&gt;element_type));</div><div>+</div><div>+    {<!-- --></div><div>+        bool       *nulls;</div><div>+</div><div>+        deconstruct_array(a, meta-&gt;element_type, meta-&gt;typlen,</div><div>+                          meta-&gt;typbyval, meta-&gt;typalign,</div><div>+                          &amp;values, &amp;nulls, &amp;nelems);</div><div>+        pfree(nulls);</div><div>+    }</div><div>+</div><div>+    for (i = 0; i &lt; nelems; i++)</div><div>+    {<!-- --></div><div>+        Datum        eq = FunctionCall2Coll(&amp;meta-&gt;eq_proc, meta-&gt;typcollation,</div><div>+                                           values[i], elem);</div><div>+</div><div>+        if (!DatumGetBool(eq))</div><div>+            values[k++] = values[i];</div><div>+    }</div><div>+</div><div>+    PG_RETURN_ARRAYTYPE_P(make_array_from_datums(values, k, meta));</div><div>+}</div><div>diff --git a/contrib/anyarray/expected/anyarray.out b/contrib/anyarray/expected/anyarray.out</div><div>new file mode 100644</div><div>index 00000000000..b17c6ef033e</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/expected/anyarray.out</div><div>@@ -0,0 +1,727 @@</div><div>+CREATE EXTENSION anyarray;</div><div>+--</div><div>+-- Phase 1: set operations, helpers and operators for int8, uuid, text</div><div>+--</div><div>+-- ===== int8 =====</div><div>+SELECT anyarray_sort(ARRAY[3, 1, 2, 1]::int8[]);</div><div>+ anyarray_sort </div><div>+---------------</div><div>+ {1,1,2,3}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_sort(ARRAY[3, 1, 2, 1]::int8[], 'desc');</div><div>+ anyarray_sort </div><div>+---------------</div><div>+ {3,2,1,1}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_sort(ARRAY[]::int8[]);</div><div>+ anyarray_sort </div><div>+---------------</div><div>+ {}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_uniq(ARRAY[1, 1, 2, 3, 3, 2]::int8[]);</div><div>+ anyarray_uniq </div><div>+---------------</div><div>+ {1,2,3}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_idx(ARRAY[10, 20, 30]::int8[], 20::int8);</div><div>+ anyarray_idx </div><div>+--------------</div><div>+            2</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_idx(ARRAY[10, 20, 30]::int8[], 99::int8);</div><div>+ anyarray_idx </div><div>+--------------</div><div>+            0</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_subarray(ARRAY[1,2,3,4,5]::int8[], 2, 2);</div><div>+ anyarray_subarray </div><div>+-------------------</div><div>+ {2,3}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_subarray(ARRAY[1,2,3,4,5]::int8[], 3);</div><div>+ anyarray_subarray </div><div>+-------------------</div><div>+ {3,4,5}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_subarray(ARRAY[1,2,3]::int8[], 5, 1);</div><div>+ anyarray_subarray </div><div>+-------------------</div><div>+ {}</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] &amp; ARRAY[2,3,4]::int8[];</div><div>+ ?column? </div><div>+----------</div><div>+ {2,3}</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] | ARRAY[3,4,5]::int8[];</div><div>+  ?column?   </div><div>+-------------</div><div>+ {1,2,3,4,5}</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] | 4::int8;</div><div>+ ?column?  </div><div>+-----------</div><div>+ {1,2,3,4}</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3,4]::int8[] - ARRAY[2,4]::int8[];</div><div>+ ?column? </div><div>+----------</div><div>+ {1,3}</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,2,3,2]::int8[] - 2::int8;</div><div>+ ?column? </div><div>+----------</div><div>+ {1,3}</div><div>+(1 row)</div><div>+</div><div>+SELECT # ARRAY[10,20,30]::int8[];</div><div>+ ?column? </div><div>+----------</div><div>+        3</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[10,20,30]::int8[] # 20::int8;</div><div>+ ?column? </div><div>+----------</div><div>+        2</div><div>+(1 row)</div><div>+</div><div>+-- ===== uuid =====</div><div>+SELECT anyarray_sort(ARRAY[</div><div>+  '00000000-0000-0000-0000-000000000003'::uuid,</div><div>+  '00000000-0000-0000-0000-000000000001',</div><div>+  '00000000-0000-0000-0000-000000000002']);</div><div>+                                                  anyarray_sort                                                   </div><div>+------------------------------------------------------------------------------------------------------------------</div><div>+ {00000000-0000-0000-0000-000000000001,00000000-0000-0000-0000-000000000002,00000000-0000-0000-0000-000000000003}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_uniq(ARRAY[</div><div>+  '00000000-0000-0000-0000-000000000001'::uuid,</div><div>+  '00000000-0000-0000-0000-000000000001',</div><div>+  '00000000-0000-0000-0000-000000000002']);</div><div>+                                anyarray_uniq                                </div><div>+-----------------------------------------------------------------------------</div><div>+ {00000000-0000-0000-0000-000000000001,00000000-0000-0000-0000-000000000002}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_idx(ARRAY[</div><div>+  '00000000-0000-0000-0000-000000000001'::uuid,</div><div>+  '00000000-0000-0000-0000-000000000002'],</div><div>+  '00000000-0000-0000-0000-000000000002'::uuid);</div><div>+ anyarray_idx </div><div>+--------------</div><div>+            2</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY['00000000-0000-0000-0000-000000000001'::uuid,</div><div>+             '00000000-0000-0000-0000-000000000002']</div><div>+     &amp; ARRAY['00000000-0000-0000-0000-000000000002'::uuid,</div><div>+             '00000000-0000-0000-0000-000000000003'];</div><div>+                ?column?                </div><div>+----------------------------------------</div><div>+ {00000000-0000-0000-0000-000000000002}</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY['00000000-0000-0000-0000-000000000001'::uuid]</div><div>+     | '00000000-0000-0000-0000-000000000002'::uuid;</div><div>+                                  ?column?                                   </div><div>+-----------------------------------------------------------------------------</div><div>+ {00000000-0000-0000-0000-000000000001,00000000-0000-0000-0000-000000000002}</div><div>+(1 row)</div><div>+</div><div>+-- ===== text =====</div><div>+SELECT anyarray_sort(ARRAY['banana','apple','cherry']);</div><div>+     anyarray_sort     </div><div>+-----------------------</div><div>+ {apple,banana,cherry}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_sort(ARRAY['banana','apple','cherry'], 'desc');</div><div>+     anyarray_sort     </div><div>+-----------------------</div><div>+ {cherry,banana,apple}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_uniq(ARRAY['a','b','a','c','b']);</div><div>+ anyarray_uniq </div><div>+---------------</div><div>+ {a,b,c}</div><div>+(1 row)</div><div>+</div><div>+SELECT anyarray_idx(ARRAY['a','b','c'], 'b'::text);</div><div>+ anyarray_idx </div><div>+--------------</div><div>+            2</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY['a','b','c']::text[] &amp; ARRAY['b','c','d']::text[];</div><div>+ ?column? </div><div>+----------</div><div>+ {b,c}</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY['a','b','c']::text[] | 'd'::text;</div><div>+ ?column?  </div><div>+-----------</div><div>+ {a,b,c,d}</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY['a','b','c','b']::text[] - 'b'::text;</div><div>+ ?column? </div><div>+----------</div><div>+ {a,c}</div><div>+(1 row)</div><div>+</div><div>+-- ===== error cases =====</div><div>+-- multi-dim</div><div>+SELECT anyarray_sort(ARRAY[[1,2],[3,4]]::int8[]);</div><div>+ERROR:  multidimensional arrays are not supported</div><div>+-- nulls</div><div>+SELECT anyarray_sort(ARRAY[1, NULL, 2]::int8[]);</div><div>+ERROR:  array must not contain nulls</div><div>+-- bad direction</div><div>+SELECT anyarray_sort(ARRAY[1,2,3]::int8[], 'bogus');</div><div>+ERROR:  second parameter must be "asc" or "desc"</div><div>+--</div><div>+-- Phase 2: anyquery and @@ operator</div><div>+--</div><div>+-- parsing + output round-trip</div><div>+SELECT '1 &amp; 2'::anyquery::text;</div><div>+ text  </div><div>+-------</div><div>+ 1 &amp; 2</div><div>+(1 row)</div><div>+</div><div>+SELECT '1 &amp; 2 | 3'::anyquery::text;</div><div>+   text    </div><div>+-----------</div><div>+ 1 &amp; 2 | 3</div><div>+(1 row)</div><div>+</div><div>+SELECT '(1 | 2) &amp; 3'::anyquery::text;</div><div>+     text      </div><div>+---------------</div><div>+ ( 1 | 2 ) &amp; 3</div><div>+(1 row)</div><div>+</div><div>+SELECT '!1 &amp; 2'::anyquery::text;</div><div>+  text  </div><div>+--------</div><div>+ !1 &amp; 2</div><div>+(1 row)</div><div>+</div><div>+SELECT '!(1 | 2) &amp; 3'::anyquery::text;</div><div>+        text        </div><div>+--------------------</div><div>+ !( ( 1 | 2 ) ) &amp; 3</div><div>+(1 row)</div><div>+</div><div>+SELECT '"hello world" | foo'::anyquery::text;</div><div>+        text         </div><div>+---------------------</div><div>+ "hello world" | foo</div><div>+(1 row)</div><div>+</div><div>+SELECT anyquery_querytree('1 &amp; 2 | 3'::anyquery);</div><div>+ anyquery_querytree </div><div>+--------------------</div><div>+ 1 2 &amp; 3 |</div><div>+(1 row)</div><div>+</div><div>+-- int8 matching</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '1'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '1 &amp; 2'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '1 &amp; 4'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ f</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '1 | 4'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '!4'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '!1'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ f</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '!4 &amp; 1'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '(1 | 4) &amp; (2 | 5)'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+-- commutator</div><div>+SELECT '1 &amp; 2'::anyquery ~~ ARRAY[1,2,3]::int8[];</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+SELECT '1 &amp; 4'::anyquery ~~ ARRAY[1,2,3]::int8[];</div><div>+ ?column? </div><div>+----------</div><div>+ f</div><div>+(1 row)</div><div>+</div><div>+-- uuid</div><div>+SELECT ARRAY['00000000-0000-0000-0000-000000000001'::uuid,</div><div>+             '00000000-0000-0000-0000-000000000002']</div><div>+       @@ '00000000-0000-0000-0000-000000000001 &amp; 00000000-0000-0000-0000-000000000002'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY['00000000-0000-0000-0000-000000000001'::uuid]</div><div>+       @@ '00000000-0000-0000-0000-000000000002'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ f</div><div>+(1 row)</div><div>+</div><div>+-- text</div><div>+SELECT ARRAY['apple','banana','cherry']::text[] @@ 'apple &amp; banana'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY['apple','banana','cherry']::text[] @@ '"banana" | grape'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY['apple','banana','cherry']::text[] @@ 'durian | grape'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ f</div><div>+(1 row)</div><div>+</div><div>+-- empty array</div><div>+SELECT ARRAY[]::int8[] @@ '1'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ f</div><div>+(1 row)</div><div>+</div><div>+SELECT ARRAY[]::int8[] @@ '!1'::anyquery;</div><div>+ ?column? </div><div>+----------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+-- parse errors</div><div>+SELECT ''::anyquery;</div><div>+ERROR:  unexpected end of input in anyquery</div><div>+LINE 1: SELECT ''::anyquery;</div><div>+               ^</div><div>+SELECT '1 &amp;'::anyquery;</div><div>+ERROR:  unexpected end of input in anyquery</div><div>+LINE 1: SELECT '1 &amp;'::anyquery;</div><div>+               ^</div><div>+SELECT '1 &amp; (2'::anyquery;</div><div>+ERROR:  missing closing parenthesis in anyquery</div><div>+LINE 1: SELECT '1 &amp; (2'::anyquery;</div><div>+               ^</div><div>+SELECT '1 ) 2'::anyquery;</div><div>+ERROR:  unexpected trailing input in anyquery</div><div>+LINE 1: SELECT '1 ) 2'::anyquery;</div><div>+               ^</div><div>+-- runtime errors: token cannot be parsed as element type</div><div>+SELECT ARRAY[1,2]::int8[] @@ 'abc'::anyquery;</div><div>+ERROR:  invalid input syntax for type bigint: "abc"</div><div>+--</div><div>+-- Phase 3: GiST signature index</div><div>+--</div><div>+-- amvalidate: well-formed opclass</div><div>+SELECT amname, opcname FROM pg_opclass opc</div><div>+LEFT JOIN pg_am am ON am.oid = opcmethod</div><div>+WHERE opc.opcname = 'anyarray_gist_ops' AND NOT amvalidate(opc.oid);</div><div>+ amname | opcname </div><div>+--------+---------</div><div>+(0 rows)</div><div>+</div><div>+-- int8 GiST: build deterministic table</div><div>+CREATE TABLE anyarray_gist_int8 (id int, a int8[]);</div><div>+INSERT INTO anyarray_gist_int8 VALUES</div><div>+  (1, ARRAY[1,2,3]::int8[]),</div><div>+  (2, ARRAY[10,20,30]::int8[]),</div><div>+  (3, ARRAY[10,20,30]::int8[]),</div><div>+  (4, ARRAY[1,2,3,4,5]::int8[]),</div><div>+  (5, ARRAY[100,200]::int8[]);</div><div>+CREATE INDEX anyarray_gist_int8_idx</div><div>+  ON anyarray_gist_int8 USING gist(a anyarray_gist_ops);</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a @&gt; ARRAY[1,2]::int8[] ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  1</div><div>+  4</div><div>+(2 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a &amp;&amp; ARRAY[10,200]::int8[] ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  2</div><div>+  3</div><div>+  5</div><div>+(3 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a = ARRAY[10,20,30]::int8[] ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  2</div><div>+  3</div><div>+(2 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a &lt;@ ARRAY[1,2,3,4,5]::int8[] ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  1</div><div>+  4</div><div>+(2 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a @@ '10 &amp; 20'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  2</div><div>+  3</div><div>+(2 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a @@ '1 | 100'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  1</div><div>+  4</div><div>+  5</div><div>+(3 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a @@ '!1 &amp; 10'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  2</div><div>+  3</div><div>+(2 rows)</div><div>+</div><div>+-- result consistency: GiST must agree with seqscan</div><div>+SELECT (</div><div>+  SELECT count(*) FROM anyarray_gist_int8 WHERE a @&gt; ARRAY[10]::int8[]</div><div>+) = (</div><div>+  SELECT count(*) FROM (SELECT 1 FROM anyarray_gist_int8 WHERE a @&gt; ARRAY[10]::int8[]) s</div><div>+) AS gist_matches_seqscan;</div><div>+ gist_matches_seqscan </div><div>+----------------------</div><div>+ t</div><div>+(1 row)</div><div>+</div><div>+RESET enable_seqscan;</div><div>+-- text GiST</div><div>+CREATE TABLE anyarray_gist_text (id int, a text[]);</div><div>+INSERT INTO anyarray_gist_text VALUES</div><div>+  (1, ARRAY['apple','banana']),</div><div>+  (2, ARRAY['banana','cherry']),</div><div>+  (3, ARRAY['apple','cherry','durian']);</div><div>+CREATE INDEX anyarray_gist_text_idx</div><div>+  ON anyarray_gist_text USING gist(a anyarray_gist_ops(siglen=64));</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_gist_text WHERE a @&gt; ARRAY['apple']::text[] ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  1</div><div>+  3</div><div>+(2 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gist_text WHERE a @@ 'apple &amp; cherry'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  3</div><div>+(1 row)</div><div>+</div><div>+RESET enable_seqscan;</div><div>+-- siglen bounds</div><div>+CREATE INDEX ON anyarray_gist_text USING gist(a anyarray_gist_ops(siglen=0));</div><div>+ERROR:  value 0 out of bounds for option "siglen"</div><div>+DETAIL:  Valid values are between "1" and "2024".</div><div>+CREATE INDEX ON anyarray_gist_text USING gist(a anyarray_gist_ops(siglen=8193));</div><div>+ERROR:  value 8193 out of bounds for option "siglen"</div><div>+DETAIL:  Valid values are between "1" and "2024".</div><div>+--</div><div>+-- Phase 4: GIN index (per-type, supports standard ops + @@)</div><div>+--</div><div>+-- amvalidate: all three opclasses</div><div>+SELECT amname, opcname FROM pg_opclass opc</div><div>+LEFT JOIN pg_am am ON am.oid = opcmethod</div><div>+WHERE opc.opcname LIKE '%anyquery_gin_ops' AND NOT amvalidate(opc.oid);</div><div>+ amname | opcname </div><div>+--------+---------</div><div>+(0 rows)</div><div>+</div><div>+-- int8 GIN</div><div>+CREATE TABLE anyarray_gin_int8 (id int, a int8[]);</div><div>+INSERT INTO anyarray_gin_int8 VALUES</div><div>+  (1, ARRAY[1,2,3]::int8[]),</div><div>+  (2, ARRAY[10,20,30]::int8[]),</div><div>+  (3, ARRAY[10,20,30]::int8[]),</div><div>+  (4, ARRAY[1,2,3,4,5]::int8[]),</div><div>+  (5, ARRAY[100,200]::int8[]);</div><div>+CREATE INDEX anyarray_gin_int8_idx</div><div>+  ON anyarray_gin_int8 USING gin(a int8_anyquery_gin_ops);</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_gin_int8 WHERE a @&gt; ARRAY[1,2]::int8[] ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  1</div><div>+  4</div><div>+(2 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gin_int8 WHERE a &amp;&amp; ARRAY[10,200]::int8[] ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  2</div><div>+  3</div><div>+  5</div><div>+(3 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gin_int8 WHERE a @@ '10 &amp; 20'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  2</div><div>+  3</div><div>+(2 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gin_int8 WHERE a @@ '(1 &amp; 2) | 100'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  1</div><div>+  4</div><div>+  5</div><div>+(3 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gin_int8 WHERE a @@ '!1'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  2</div><div>+  3</div><div>+  5</div><div>+(3 rows)</div><div>+</div><div>+-- uuid GIN</div><div>+CREATE TABLE anyarray_gin_uuid (id int, a uuid[]);</div><div>+INSERT INTO anyarray_gin_uuid VALUES</div><div>+  (1, ARRAY['11111111-1111-1111-1111-111111111111'::uuid,</div><div>+            '22222222-2222-2222-2222-222222222222']),</div><div>+  (2, ARRAY['33333333-3333-3333-3333-333333333333'::uuid]),</div><div>+  (3, ARRAY['11111111-1111-1111-1111-111111111111'::uuid,</div><div>+            '33333333-3333-3333-3333-333333333333']);</div><div>+CREATE INDEX anyarray_gin_uuid_idx</div><div>+  ON anyarray_gin_uuid USING gin(a uuid_anyquery_gin_ops);</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_gin_uuid</div><div>+  WHERE a @@ '11111111-1111-1111-1111-111111111111 &amp; 22222222-2222-2222-2222-222222222222'::anyquery</div><div>+  ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  1</div><div>+(1 row)</div><div>+</div><div>+SELECT id FROM anyarray_gin_uuid</div><div>+  WHERE a @&gt; ARRAY['11111111-1111-1111-1111-111111111111'::uuid]</div><div>+  ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  1</div><div>+  3</div><div>+(2 rows)</div><div>+</div><div>+RESET enable_seqscan;</div><div>+-- text GIN</div><div>+CREATE TABLE anyarray_gin_text (id int, a text[]);</div><div>+INSERT INTO anyarray_gin_text VALUES</div><div>+  (1, ARRAY['apple','banana']),</div><div>+  (2, ARRAY['banana','cherry']),</div><div>+  (3, ARRAY['apple','cherry','durian']);</div><div>+CREATE INDEX anyarray_gin_text_idx</div><div>+  ON anyarray_gin_text USING gin(a text_anyquery_gin_ops);</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_gin_text WHERE a @@ 'apple &amp; cherry'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  3</div><div>+(1 row)</div><div>+</div><div>+SELECT id FROM anyarray_gin_text WHERE a @@ '"durian"'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  3</div><div>+(1 row)</div><div>+</div><div>+SELECT id FROM anyarray_gin_text WHERE a @@ 'apple | grape'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  1</div><div>+  3</div><div>+(2 rows)</div><div>+</div><div>+SELECT id FROM anyarray_gin_text WHERE a @@ '!banana'::anyquery ORDER BY id;</div><div>+ id </div><div>+----</div><div>+  3</div><div>+(1 row)</div><div>+</div><div>+RESET enable_seqscan;</div><div>+--</div><div>+-- Phase 5: cross-AM consistency and edge cases</div><div>+--</div><div>+-- Build a larger deterministic int8 table and index it with both GiST and</div><div>+-- GIN.  Then run each candidate query under seqscan, GiST and GIN and check</div><div>+-- that all three plans return the same row set.</div><div>+--</div><div>+CREATE TABLE anyarray_xcheck (id int, a int8[]);</div><div>+INSERT INTO anyarray_xcheck</div><div>+SELECT g,</div><div>+       ARRAY[(g % 7)::int8, ((g * 3) % 11)::int8, ((g + 1) % 5)::int8]</div><div>+FROM generate_series(1, 200) g;</div><div>+-- a few hand-picked rows to exercise common values</div><div>+INSERT INTO anyarray_xcheck VALUES</div><div>+  (1001, ARRAY[1,2,3,4,5]::int8[]),</div><div>+  (1002, ARRAY[1,2,3,4,5]::int8[]),</div><div>+  (1003, ARRAY[100,200,300]::int8[]),</div><div>+  (1004, ARRAY[]::int8[]),</div><div>+  (1005, NULL);</div><div>+CREATE INDEX anyarray_xcheck_gist ON anyarray_xcheck</div><div>+  USING gist(a anyarray_gist_ops);</div><div>+CREATE INDEX anyarray_xcheck_gin ON anyarray_xcheck</div><div>+  USING gin(a int8_anyquery_gin_ops);</div><div>+-- Run each predicate three times: pure seqscan, GiST-only, GIN-only.</div><div>+-- All three result sets must match.</div><div>+CREATE FUNCTION anyarray_xcheck_match(pred text)</div><div>+RETURNS TABLE(gist_ok bool, gin_ok bool)</div><div>+LANGUAGE plpgsql AS $$</div><div>+DECLARE</div><div>+  seq int[]; gst int[]; gin int[];</div><div>+BEGIN</div><div>+  SET LOCAL enable_seqscan = on;</div><div>+  SET LOCAL enable_indexscan = off;</div><div>+  SET LOCAL enable_bitmapscan = off;</div><div>+  EXECUTE 'SELECT array_agg(id ORDER BY id) FROM anyarray_xcheck WHERE '</div><div>+          || pred INTO seq;</div><div>+</div><div>+  -- Force GiST by hiding the GIN index.</div><div>+  ALTER INDEX anyarray_xcheck_gin SET (fastupdate = off);</div><div>+  SET LOCAL enable_seqscan = off;</div><div>+  SET LOCAL enable_indexscan = on;</div><div>+  SET LOCAL enable_bitmapscan = on;</div><div>+  DROP INDEX anyarray_xcheck_gin;</div><div>+  EXECUTE 'SELECT array_agg(id ORDER BY id) FROM anyarray_xcheck WHERE '</div><div>+          || pred INTO gst;</div><div>+  CREATE INDEX anyarray_xcheck_gin ON anyarray_xcheck</div><div>+    USING gin(a int8_anyquery_gin_ops);</div><div>+</div><div>+  -- Force GIN by hiding GiST.</div><div>+  DROP INDEX anyarray_xcheck_gist;</div><div>+  EXECUTE 'SELECT array_agg(id ORDER BY id) FROM anyarray_xcheck WHERE '</div><div>+          || pred INTO gin;</div><div>+  CREATE INDEX anyarray_xcheck_gist ON anyarray_xcheck</div><div>+    USING gist(a anyarray_gist_ops);</div><div>+</div><div>+  gist_ok := seq IS NOT DISTINCT FROM gst;</div><div>+  gin_ok  := seq IS NOT DISTINCT FROM gin;</div><div>+  RETURN NEXT;</div><div>+END $$;</div><div>+SELECT pred, gist_ok, gin_ok FROM (VALUES</div><div>+  ('a @&gt; ARRAY[1,2]::int8[]'),</div><div>+  ('a @&gt; ARRAY[100,200]::int8[]'),</div><div>+  ('a &amp;&amp; ARRAY[5,99]::int8[]'),</div><div>+  ('a = ARRAY[1,2,3,4,5]::int8[]'),</div><div>+  ('a @@ ''1 &amp; 2''::anyquery'),</div><div>+  ('a @@ ''100 | 999''::anyquery'),</div><div>+  ('a @@ ''!1 &amp; 2''::anyquery')</div><div>+) v(pred), LATERAL anyarray_xcheck_match(pred);</div><div>+             pred             | gist_ok | gin_ok </div><div>+------------------------------+---------+--------</div><div>+ a @&gt; ARRAY[1,2]::int8[]      | t       | t</div><div>+ a @&gt; ARRAY[100,200]::int8[]  | t       | t</div><div>+ a &amp;&amp; ARRAY[5,99]::int8[]     | t       | t</div><div>+ a = ARRAY[1,2,3,4,5]::int8[] | t       | t</div><div>+ a @@ '1 &amp; 2'::anyquery       | t       | t</div><div>+ a @@ '100 | 999'::anyquery   | t       | t</div><div>+ a @@ '!1 &amp; 2'::anyquery      | t       | t</div><div>+(7 rows)</div><div>+</div><div>+-- Edge cases: empty / NULL arrays via index</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_xcheck WHERE a @&gt; ARRAY[]::int8[] AND id = 1004;</div><div>+  id  </div><div>+------</div><div>+ 1004</div><div>+(1 row)</div><div>+</div><div>+SELECT id FROM anyarray_xcheck WHERE a &lt;@ ARRAY[1,2,3]::int8[] AND id &lt; 100 ORDER BY id LIMIT 3;</div><div>+ id </div><div>+----</div><div>+  1</div><div>+ 15</div><div>+ 30</div><div>+(3 rows)</div><div>+</div><div>+SELECT count(*) FROM anyarray_xcheck WHERE a IS NULL;</div><div>+ count </div><div>+-------</div><div>+     1</div><div>+(1 row)</div><div>+</div><div>+RESET enable_seqscan;</div><div>+-- DELETE / VACUUM / re-query through index</div><div>+DELETE FROM anyarray_xcheck WHERE id BETWEEN 1 AND 50;</div><div>+VACUUM anyarray_xcheck;</div><div>+SET enable_seqscan = off;</div><div>+SELECT count(*) FROM anyarray_xcheck WHERE a @@ '1 | 2'::anyquery;</div><div>+ count </div><div>+-------</div><div>+    98</div><div>+(1 row)</div><div>+</div><div>+RESET enable_seqscan;</div><div>+DROP FUNCTION anyarray_xcheck_match(text);</div><div>diff --git a/contrib/anyarray/meson.build b/contrib/anyarray/meson.build</div><div>new file mode 100644</div><div>index 00000000000..a20646b82b2</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/meson.build</div><div>@@ -0,0 +1,38 @@</div><div>+# Copyright (c) 2026, PostgreSQL Global Development Group</div><div>+</div><div>+anyarray_sources = files(</div><div>+  'anyarray.c',</div><div>+  'anyarray_bool.c',</div><div>+  'anyarray_gin.c',</div><div>+  'anyarray_gist.c',</div><div>+  'anyarray_op.c',</div><div>+)</div><div>+</div><div>+if host_system == 'windows'</div><div>+  anyarray_sources += rc_lib_gen.process(win32ver_rc, extra_args: [</div><div>+    '--NAME', 'anyarray',</div><div>+    '--FILEDESC', 'anyarray - operations and indexes for arrays of any type',])</div><div>+endif</div><div>+</div><div>+anyarray = shared_module('anyarray',</div><div>+  anyarray_sources,</div><div>+  kwargs: contrib_mod_args,</div><div>+)</div><div>+contrib_targets += anyarray</div><div>+</div><div>+install_data(</div><div>+  'anyarray--1.0.sql',</div><div>+  'anyarray.control',</div><div>+  kwargs: contrib_data_args,</div><div>+)</div><div>+</div><div>+tests += {<!-- --></div><div>+  'name': 'anyarray',</div><div>+  'sd': meson.current_source_dir(),</div><div>+  'bd': meson.current_build_dir(),</div><div>+  'regress': {<!-- --></div><div>+    'sql': [</div><div>+      'anyarray',</div><div>+    ],</div><div>+  },</div><div>+}</div><div>diff --git a/contrib/anyarray/sql/anyarray.sql b/contrib/anyarray/sql/anyarray.sql</div><div>new file mode 100644</div><div>index 00000000000..90cc061fbde</div><div>--- /dev/null</div><div>+++ b/contrib/anyarray/sql/anyarray.sql</div><div>@@ -0,0 +1,330 @@</div><div>+CREATE EXTENSION anyarray;</div><div>+</div><div>+--</div><div>+-- Phase 1: set operations, helpers and operators for int8, uuid, text</div><div>+--</div><div>+</div><div>+-- ===== int8 =====</div><div>+SELECT anyarray_sort(ARRAY[3, 1, 2, 1]::int8[]);</div><div>+SELECT anyarray_sort(ARRAY[3, 1, 2, 1]::int8[], 'desc');</div><div>+SELECT anyarray_sort(ARRAY[]::int8[]);</div><div>+SELECT anyarray_uniq(ARRAY[1, 1, 2, 3, 3, 2]::int8[]);</div><div>+SELECT anyarray_idx(ARRAY[10, 20, 30]::int8[], 20::int8);</div><div>+SELECT anyarray_idx(ARRAY[10, 20, 30]::int8[], 99::int8);</div><div>+SELECT anyarray_subarray(ARRAY[1,2,3,4,5]::int8[], 2, 2);</div><div>+SELECT anyarray_subarray(ARRAY[1,2,3,4,5]::int8[], 3);</div><div>+SELECT anyarray_subarray(ARRAY[1,2,3]::int8[], 5, 1);</div><div>+</div><div>+SELECT ARRAY[1,2,3]::int8[] &amp; ARRAY[2,3,4]::int8[];</div><div>+SELECT ARRAY[1,2,3]::int8[] | ARRAY[3,4,5]::int8[];</div><div>+SELECT ARRAY[1,2,3]::int8[] | 4::int8;</div><div>+SELECT ARRAY[1,2,3,4]::int8[] - ARRAY[2,4]::int8[];</div><div>+SELECT ARRAY[1,2,2,3,2]::int8[] - 2::int8;</div><div>+SELECT # ARRAY[10,20,30]::int8[];</div><div>+SELECT ARRAY[10,20,30]::int8[] # 20::int8;</div><div>+</div><div>+-- ===== uuid =====</div><div>+SELECT anyarray_sort(ARRAY[</div><div>+  '00000000-0000-0000-0000-000000000003'::uuid,</div><div>+  '00000000-0000-0000-0000-000000000001',</div><div>+  '00000000-0000-0000-0000-000000000002']);</div><div>+SELECT anyarray_uniq(ARRAY[</div><div>+  '00000000-0000-0000-0000-000000000001'::uuid,</div><div>+  '00000000-0000-0000-0000-000000000001',</div><div>+  '00000000-0000-0000-0000-000000000002']);</div><div>+SELECT anyarray_idx(ARRAY[</div><div>+  '00000000-0000-0000-0000-000000000001'::uuid,</div><div>+  '00000000-0000-0000-0000-000000000002'],</div><div>+  '00000000-0000-0000-0000-000000000002'::uuid);</div><div>+</div><div>+SELECT ARRAY['00000000-0000-0000-0000-000000000001'::uuid,</div><div>+             '00000000-0000-0000-0000-000000000002']</div><div>+     &amp; ARRAY['00000000-0000-0000-0000-000000000002'::uuid,</div><div>+             '00000000-0000-0000-0000-000000000003'];</div><div>+</div><div>+SELECT ARRAY['00000000-0000-0000-0000-000000000001'::uuid]</div><div>+     | '00000000-0000-0000-0000-000000000002'::uuid;</div><div>+</div><div>+-- ===== text =====</div><div>+SELECT anyarray_sort(ARRAY['banana','apple','cherry']);</div><div>+SELECT anyarray_sort(ARRAY['banana','apple','cherry'], 'desc');</div><div>+SELECT anyarray_uniq(ARRAY['a','b','a','c','b']);</div><div>+SELECT anyarray_idx(ARRAY['a','b','c'], 'b'::text);</div><div>+SELECT ARRAY['a','b','c']::text[] &amp; ARRAY['b','c','d']::text[];</div><div>+SELECT ARRAY['a','b','c']::text[] | 'd'::text;</div><div>+SELECT ARRAY['a','b','c','b']::text[] - 'b'::text;</div><div>+</div><div>+-- ===== error cases =====</div><div>+-- multi-dim</div><div>+SELECT anyarray_sort(ARRAY[[1,2],[3,4]]::int8[]);</div><div>+-- nulls</div><div>+SELECT anyarray_sort(ARRAY[1, NULL, 2]::int8[]);</div><div>+-- bad direction</div><div>+SELECT anyarray_sort(ARRAY[1,2,3]::int8[], 'bogus');</div><div>+</div><div>+--</div><div>+-- Phase 2: anyquery and @@ operator</div><div>+--</div><div>+</div><div>+-- parsing + output round-trip</div><div>+SELECT '1 &amp; 2'::anyquery::text;</div><div>+SELECT '1 &amp; 2 | 3'::anyquery::text;</div><div>+SELECT '(1 | 2) &amp; 3'::anyquery::text;</div><div>+SELECT '!1 &amp; 2'::anyquery::text;</div><div>+SELECT '!(1 | 2) &amp; 3'::anyquery::text;</div><div>+SELECT '"hello world" | foo'::anyquery::text;</div><div>+SELECT anyquery_querytree('1 &amp; 2 | 3'::anyquery);</div><div>+</div><div>+-- int8 matching</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '1'::anyquery;</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '1 &amp; 2'::anyquery;</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '1 &amp; 4'::anyquery;</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '1 | 4'::anyquery;</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '!4'::anyquery;</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '!1'::anyquery;</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '!4 &amp; 1'::anyquery;</div><div>+SELECT ARRAY[1,2,3]::int8[] @@ '(1 | 4) &amp; (2 | 5)'::anyquery;</div><div>+</div><div>+-- commutator</div><div>+SELECT '1 &amp; 2'::anyquery ~~ ARRAY[1,2,3]::int8[];</div><div>+SELECT '1 &amp; 4'::anyquery ~~ ARRAY[1,2,3]::int8[];</div><div>+</div><div>+-- uuid</div><div>+SELECT ARRAY['00000000-0000-0000-0000-000000000001'::uuid,</div><div>+             '00000000-0000-0000-0000-000000000002']</div><div>+       @@ '00000000-0000-0000-0000-000000000001 &amp; 00000000-0000-0000-0000-000000000002'::anyquery;</div><div>+SELECT ARRAY['00000000-0000-0000-0000-000000000001'::uuid]</div><div>+       @@ '00000000-0000-0000-0000-000000000002'::anyquery;</div><div>+</div><div>+-- text</div><div>+SELECT ARRAY['apple','banana','cherry']::text[] @@ 'apple &amp; banana'::anyquery;</div><div>+SELECT ARRAY['apple','banana','cherry']::text[] @@ '"banana" | grape'::anyquery;</div><div>+SELECT ARRAY['apple','banana','cherry']::text[] @@ 'durian | grape'::anyquery;</div><div>+</div><div>+-- empty array</div><div>+SELECT ARRAY[]::int8[] @@ '1'::anyquery;</div><div>+SELECT ARRAY[]::int8[] @@ '!1'::anyquery;</div><div>+</div><div>+-- parse errors</div><div>+SELECT ''::anyquery;</div><div>+SELECT '1 &amp;'::anyquery;</div><div>+SELECT '1 &amp; (2'::anyquery;</div><div>+SELECT '1 ) 2'::anyquery;</div><div>+</div><div>+-- runtime errors: token cannot be parsed as element type</div><div>+SELECT ARRAY[1,2]::int8[] @@ 'abc'::anyquery;</div><div>+</div><div>+--</div><div>+-- Phase 3: GiST signature index</div><div>+--</div><div>+</div><div>+-- amvalidate: well-formed opclass</div><div>+SELECT amname, opcname FROM pg_opclass opc</div><div>+LEFT JOIN pg_am am ON am.oid = opcmethod</div><div>+WHERE opc.opcname = 'anyarray_gist_ops' AND NOT amvalidate(opc.oid);</div><div>+</div><div>+-- int8 GiST: build deterministic table</div><div>+CREATE TABLE anyarray_gist_int8 (id int, a int8[]);</div><div>+INSERT INTO anyarray_gist_int8 VALUES</div><div>+  (1, ARRAY[1,2,3]::int8[]),</div><div>+  (2, ARRAY[10,20,30]::int8[]),</div><div>+  (3, ARRAY[10,20,30]::int8[]),</div><div>+  (4, ARRAY[1,2,3,4,5]::int8[]),</div><div>+  (5, ARRAY[100,200]::int8[]);</div><div>+</div><div>+CREATE INDEX anyarray_gist_int8_idx</div><div>+  ON anyarray_gist_int8 USING gist(a anyarray_gist_ops);</div><div>+</div><div>+SET enable_seqscan = off;</div><div>+</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a @&gt; ARRAY[1,2]::int8[] ORDER BY id;</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a &amp;&amp; ARRAY[10,200]::int8[] ORDER BY id;</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a = ARRAY[10,20,30]::int8[] ORDER BY id;</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a &lt;@ ARRAY[1,2,3,4,5]::int8[] ORDER BY id;</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a @@ '10 &amp; 20'::anyquery ORDER BY id;</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a @@ '1 | 100'::anyquery ORDER BY id;</div><div>+SELECT id FROM anyarray_gist_int8 WHERE a @@ '!1 &amp; 10'::anyquery ORDER BY id;</div><div>+</div><div>+-- result consistency: GiST must agree with seqscan</div><div>+SELECT (</div><div>+  SELECT count(*) FROM anyarray_gist_int8 WHERE a @&gt; ARRAY[10]::int8[]</div><div>+) = (</div><div>+  SELECT count(*) FROM (SELECT 1 FROM anyarray_gist_int8 WHERE a @&gt; ARRAY[10]::int8[]) s</div><div>+) AS gist_matches_seqscan;</div><div>+</div><div>+RESET enable_seqscan;</div><div>+</div><div>+-- text GiST</div><div>+CREATE TABLE anyarray_gist_text (id int, a text[]);</div><div>+INSERT INTO anyarray_gist_text VALUES</div><div>+  (1, ARRAY['apple','banana']),</div><div>+  (2, ARRAY['banana','cherry']),</div><div>+  (3, ARRAY['apple','cherry','durian']);</div><div>+</div><div>+CREATE INDEX anyarray_gist_text_idx</div><div>+  ON anyarray_gist_text USING gist(a anyarray_gist_ops(siglen=64));</div><div>+</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_gist_text WHERE a @&gt; ARRAY['apple']::text[] ORDER BY id;</div><div>+SELECT id FROM anyarray_gist_text WHERE a @@ 'apple &amp; cherry'::anyquery ORDER BY id;</div><div>+RESET enable_seqscan;</div><div>+</div><div>+-- siglen bounds</div><div>+CREATE INDEX ON anyarray_gist_text USING gist(a anyarray_gist_ops(siglen=0));</div><div>+CREATE INDEX ON anyarray_gist_text USING gist(a anyarray_gist_ops(siglen=8193));</div><div>+</div><div>+--</div><div>+-- Phase 4: GIN index (per-type, supports standard ops + @@)</div><div>+--</div><div>+</div><div>+-- amvalidate: all three opclasses</div><div>+SELECT amname, opcname FROM pg_opclass opc</div><div>+LEFT JOIN pg_am am ON am.oid = opcmethod</div><div>+WHERE opc.opcname LIKE '%anyquery_gin_ops' AND NOT amvalidate(opc.oid);</div><div>+</div><div>+-- int8 GIN</div><div>+CREATE TABLE anyarray_gin_int8 (id int, a int8[]);</div><div>+INSERT INTO anyarray_gin_int8 VALUES</div><div>+  (1, ARRAY[1,2,3]::int8[]),</div><div>+  (2, ARRAY[10,20,30]::int8[]),</div><div>+  (3, ARRAY[10,20,30]::int8[]),</div><div>+  (4, ARRAY[1,2,3,4,5]::int8[]),</div><div>+  (5, ARRAY[100,200]::int8[]);</div><div>+</div><div>+CREATE INDEX anyarray_gin_int8_idx</div><div>+  ON anyarray_gin_int8 USING gin(a int8_anyquery_gin_ops);</div><div>+</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_gin_int8 WHERE a @&gt; ARRAY[1,2]::int8[] ORDER BY id;</div><div>+SELECT id FROM anyarray_gin_int8 WHERE a &amp;&amp; ARRAY[10,200]::int8[] ORDER BY id;</div><div>+SELECT id FROM anyarray_gin_int8 WHERE a @@ '10 &amp; 20'::anyquery ORDER BY id;</div><div>+SELECT id FROM anyarray_gin_int8 WHERE a @@ '(1 &amp; 2) | 100'::anyquery ORDER BY id;</div><div>+SELECT id FROM anyarray_gin_int8 WHERE a @@ '!1'::anyquery ORDER BY id;</div><div>+</div><div>+-- uuid GIN</div><div>+CREATE TABLE anyarray_gin_uuid (id int, a uuid[]);</div><div>+INSERT INTO anyarray_gin_uuid VALUES</div><div>+  (1, ARRAY['11111111-1111-1111-1111-111111111111'::uuid,</div><div>+            '22222222-2222-2222-2222-222222222222']),</div><div>+  (2, ARRAY['33333333-3333-3333-3333-333333333333'::uuid]),</div><div>+  (3, ARRAY['11111111-1111-1111-1111-111111111111'::uuid,</div><div>+            '33333333-3333-3333-3333-333333333333']);</div><div>+</div><div>+CREATE INDEX anyarray_gin_uuid_idx</div><div>+  ON anyarray_gin_uuid USING gin(a uuid_anyquery_gin_ops);</div><div>+</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_gin_uuid</div><div>+  WHERE a @@ '11111111-1111-1111-1111-111111111111 &amp; 22222222-2222-2222-2222-222222222222'::anyquery</div><div>+  ORDER BY id;</div><div>+SELECT id FROM anyarray_gin_uuid</div><div>+  WHERE a @&gt; ARRAY['11111111-1111-1111-1111-111111111111'::uuid]</div><div>+  ORDER BY id;</div><div>+RESET enable_seqscan;</div><div>+</div><div>+-- text GIN</div><div>+CREATE TABLE anyarray_gin_text (id int, a text[]);</div><div>+INSERT INTO anyarray_gin_text VALUES</div><div>+  (1, ARRAY['apple','banana']),</div><div>+  (2, ARRAY['banana','cherry']),</div><div>+  (3, ARRAY['apple','cherry','durian']);</div><div>+</div><div>+CREATE INDEX anyarray_gin_text_idx</div><div>+  ON anyarray_gin_text USING gin(a text_anyquery_gin_ops);</div><div>+</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_gin_text WHERE a @@ 'apple &amp; cherry'::anyquery ORDER BY id;</div><div>+SELECT id FROM anyarray_gin_text WHERE a @@ '"durian"'::anyquery ORDER BY id;</div><div>+SELECT id FROM anyarray_gin_text WHERE a @@ 'apple | grape'::anyquery ORDER BY id;</div><div>+SELECT id FROM anyarray_gin_text WHERE a @@ '!banana'::anyquery ORDER BY id;</div><div>+RESET enable_seqscan;</div><div>+</div><div>+--</div><div>+-- Phase 5: cross-AM consistency and edge cases</div><div>+--</div><div>+-- Build a larger deterministic int8 table and index it with both GiST and</div><div>+-- GIN.  Then run each candidate query under seqscan, GiST and GIN and check</div><div>+-- that all three plans return the same row set.</div><div>+--</div><div>+</div><div>+CREATE TABLE anyarray_xcheck (id int, a int8[]);</div><div>+</div><div>+INSERT INTO anyarray_xcheck</div><div>+SELECT g,</div><div>+       ARRAY[(g % 7)::int8, ((g * 3) % 11)::int8, ((g + 1) % 5)::int8]</div><div>+FROM generate_series(1, 200) g;</div><div>+-- a few hand-picked rows to exercise common values</div><div>+INSERT INTO anyarray_xcheck VALUES</div><div>+  (1001, ARRAY[1,2,3,4,5]::int8[]),</div><div>+  (1002, ARRAY[1,2,3,4,5]::int8[]),</div><div>+  (1003, ARRAY[100,200,300]::int8[]),</div><div>+  (1004, ARRAY[]::int8[]),</div><div>+  (1005, NULL);</div><div>+</div><div>+CREATE INDEX anyarray_xcheck_gist ON anyarray_xcheck</div><div>+  USING gist(a anyarray_gist_ops);</div><div>+CREATE INDEX anyarray_xcheck_gin ON anyarray_xcheck</div><div>+  USING gin(a int8_anyquery_gin_ops);</div><div>+</div><div>+-- Run each predicate three times: pure seqscan, GiST-only, GIN-only.</div><div>+-- All three result sets must match.</div><div>+CREATE FUNCTION anyarray_xcheck_match(pred text)</div><div>+RETURNS TABLE(gist_ok bool, gin_ok bool)</div><div>+LANGUAGE plpgsql AS $$</div><div>+DECLARE</div><div>+  seq int[]; gst int[]; gin int[];</div><div>+BEGIN</div><div>+  SET LOCAL enable_seqscan = on;</div><div>+  SET LOCAL enable_indexscan = off;</div><div>+  SET LOCAL enable_bitmapscan = off;</div><div>+  EXECUTE 'SELECT array_agg(id ORDER BY id) FROM anyarray_xcheck WHERE '</div><div>+          || pred INTO seq;</div><div>+</div><div>+  -- Force GiST by hiding the GIN index.</div><div>+  ALTER INDEX anyarray_xcheck_gin SET (fastupdate = off);</div><div>+  SET LOCAL enable_seqscan = off;</div><div>+  SET LOCAL enable_indexscan = on;</div><div>+  SET LOCAL enable_bitmapscan = on;</div><div>+  DROP INDEX anyarray_xcheck_gin;</div><div>+  EXECUTE 'SELECT array_agg(id ORDER BY id) FROM anyarray_xcheck WHERE '</div><div>+          || pred INTO gst;</div><div>+  CREATE INDEX anyarray_xcheck_gin ON anyarray_xcheck</div><div>+    USING gin(a int8_anyquery_gin_ops);</div><div>+</div><div>+  -- Force GIN by hiding GiST.</div><div>+  DROP INDEX anyarray_xcheck_gist;</div><div>+  EXECUTE 'SELECT array_agg(id ORDER BY id) FROM anyarray_xcheck WHERE '</div><div>+          || pred INTO gin;</div><div>+  CREATE INDEX anyarray_xcheck_gist ON anyarray_xcheck</div><div>+    USING gist(a anyarray_gist_ops);</div><div>+</div><div>+  gist_ok := seq IS NOT DISTINCT FROM gst;</div><div>+  gin_ok  := seq IS NOT DISTINCT FROM gin;</div><div>+  RETURN NEXT;</div><div>+END $$;</div><div>+</div><div>+SELECT pred, gist_ok, gin_ok FROM (VALUES</div><div>+  ('a @&gt; ARRAY[1,2]::int8[]'),</div><div>+  ('a @&gt; ARRAY[100,200]::int8[]'),</div><div>+  ('a &amp;&amp; ARRAY[5,99]::int8[]'),</div><div>+  ('a = ARRAY[1,2,3,4,5]::int8[]'),</div><div>+  ('a @@ ''1 &amp; 2''::anyquery'),</div><div>+  ('a @@ ''100 | 999''::anyquery'),</div><div>+  ('a @@ ''!1 &amp; 2''::anyquery')</div><div>+) v(pred), LATERAL anyarray_xcheck_match(pred);</div><div>+</div><div>+-- Edge cases: empty / NULL arrays via index</div><div>+SET enable_seqscan = off;</div><div>+SELECT id FROM anyarray_xcheck WHERE a @&gt; ARRAY[]::int8[] AND id = 1004;</div><div>+SELECT id FROM anyarray_xcheck WHERE a &lt;@ ARRAY[1,2,3]::int8[] AND id &lt; 100 ORDER BY id LIMIT 3;</div><div>+SELECT count(*) FROM anyarray_xcheck WHERE a IS NULL;</div><div>+RESET enable_seqscan;</div><div>+</div><div>+-- DELETE / VACUUM / re-query through index</div><div>+DELETE FROM anyarray_xcheck WHERE id BETWEEN 1 AND 50;</div><div>+VACUUM anyarray_xcheck;</div><div>+SET enable_seqscan = off;</div><div>+SELECT count(*) FROM anyarray_xcheck WHERE a @@ '1 | 2'::anyquery;</div><div>+RESET enable_seqscan;</div><div>+</div><div>+DROP FUNCTION anyarray_xcheck_match(text);</div><div>diff --git a/contrib/meson.build b/contrib/meson.build</div><div>index ebb7f83d8c5..e158f18fa7e 100644</div><div>--- a/contrib/meson.build</div><div>+++ b/contrib/meson.build</div><div>@@ -13,6 +13,7 @@ contrib_doc_args = {<!-- --></div><div> }</div><div> </div><div> subdir('amcheck')</div><div>+subdir('anyarray')</div><div> subdir('auth_delay')</div><div> subdir('auto_explain')</div><div> subdir('basic_archive')</div><div>diff --git a/doc/src/sgml/anyarray.sgml b/doc/src/sgml/anyarray.sgml</div><div>new file mode 100644</div><div>index 00000000000..4f376e0868c</div><div>--- /dev/null</div><div>+++ b/doc/src/sgml/anyarray.sgml</div><div>@@ -0,0 +1,482 @@</div><div>+&lt;!-- doc/src/sgml/anyarray.sgml --&gt;</div><div>+</div><div>+&lt;sect1 id="anyarray" xreflabel="anyarray"&gt;</div><div>+ &lt;title&gt;anyarray &amp;mdash; operations and indexes for arrays of any type&lt;/title&gt;</div><div>+</div><div>+ &lt;indexterm zone="anyarray"&gt;</div><div>+  &lt;primary&gt;anyarray&lt;/primary&gt;</div><div>+ &lt;/indexterm&gt;</div><div>+</div><div>+ &lt;para&gt;</div><div>+  The &lt;filename&gt;anyarray&lt;/filename&gt; module generalizes</div><div>+  &lt;xref linkend="intarray"/&gt;-style operations to arrays of any element type</div><div>+  that provides a default B-tree opclass.  It adds set-style helpers, a</div><div>+  textual boolean query type (&lt;type&gt;anyquery&lt;/type&gt;), and index support that</div><div>+  works for arbitrary element types.</div><div>+ &lt;/para&gt;</div><div>+</div><div>+ &lt;para&gt;</div><div>+  All operations reject arrays that contain &lt;literal&gt;NULL&lt;/literal&gt; elements,</div><div>+  and they are defined only for one-dimensional arrays.</div><div>+ &lt;/para&gt;</div><div>+</div><div>+ &lt;para&gt;</div><div>+  This module is not marked as &lt;quote&gt;trusted&lt;/quote&gt;: installing it</div><div>+  requires database-level privileges, because the index operator classes</div><div>+  it defines refer to internal storage types.</div><div>+ &lt;/para&gt;</div><div>+</div><div>+ &lt;sect2 id="anyarray-funcs-ops"&gt;</div><div>+  &lt;title&gt;&lt;filename&gt;anyarray&lt;/filename&gt; Functions and Operators&lt;/title&gt;</div><div>+</div><div>+  &lt;para&gt;</div><div>+   The functions provided by &lt;filename&gt;anyarray&lt;/filename&gt; are listed in</div><div>+   &lt;xref linkend="anyarray-func-table"/&gt;, the operators in</div><div>+   &lt;xref linkend="anyarray-op-table"/&gt;.</div><div>+  &lt;/para&gt;</div><div>+</div><div>+  &lt;table id="anyarray-func-table"&gt;</div><div>+   &lt;title&gt;&lt;filename&gt;anyarray&lt;/filename&gt; Functions&lt;/title&gt;</div><div>+    &lt;tgroup cols="1"&gt;</div><div>+     &lt;thead&gt;</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        Function</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Description</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Example(s)</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+     &lt;/thead&gt;</div><div>+</div><div>+     &lt;tbody&gt;</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyarray_sort&lt;/function&gt; ( &lt;type&gt;anyarray&lt;/type&gt; [, &lt;type&gt;text&lt;/type&gt; ] )</div><div>+        &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Sorts an array.  The optional second argument is</div><div>+        &lt;literal&gt;asc&lt;/literal&gt; (default) or &lt;literal&gt;desc&lt;/literal&gt;.</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyarray_sort('{3,1,2}'::int8[])&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;{1,2,3}&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyarray_uniq&lt;/function&gt; ( &lt;type&gt;anyarray&lt;/type&gt; )</div><div>+        &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Removes duplicates from the array, returning the sorted unique</div><div>+        elements.</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyarray_uniq('{1,1,2,3,3}'::int8[])&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;{1,2,3}&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyarray_idx&lt;/function&gt; ( &lt;type&gt;anyarray&lt;/type&gt;, &lt;type&gt;anyelement&lt;/type&gt; )</div><div>+        &lt;returnvalue&gt;integer&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Returns the 1-based index of the first occurrence of the element,</div><div>+        or 0 if not found.</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyarray_idx(ARRAY['a','b','c'], 'b')&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;2&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyarray_subarray&lt;/function&gt; ( &lt;type&gt;anyarray&lt;/type&gt;, &lt;parameter&gt;start&lt;/parameter&gt; &lt;type&gt;integer&lt;/type&gt; [, &lt;parameter&gt;length&lt;/parameter&gt; &lt;type&gt;integer&lt;/type&gt; ] )</div><div>+        &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Returns a contiguous slice starting at &lt;parameter&gt;start&lt;/parameter&gt;</div><div>+        (1-based).  If &lt;parameter&gt;length&lt;/parameter&gt; is omitted the slice</div><div>+        extends to the end of the array.  Out-of-range positions or</div><div>+        non-positive lengths produce an empty array.</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyarray_subarray('{1,2,3,4,5}'::int8[], 2, 2)&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;{2,3}&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyarray_icount&lt;/function&gt; ( &lt;type&gt;anyarray&lt;/type&gt; )</div><div>+        &lt;returnvalue&gt;integer&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Returns the number of elements in the array (exposed under the</div><div>+        &lt;literal&gt;#&lt;/literal&gt; operator).</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyarray_icount('{10,20,30}'::int8[])&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;3&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyarray_intersect&lt;/function&gt; ( &lt;type&gt;anyarray&lt;/type&gt;, &lt;type&gt;anyarray&lt;/type&gt; )</div><div>+        &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Returns the sorted, deduplicated values present in both inputs.</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyarray_intersect('{1,2,3}'::int8[], '{2,3,4}')&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;{2,3}&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyarray_union&lt;/function&gt; ( &lt;type&gt;anyarray&lt;/type&gt;, &lt;type&gt;anyarray&lt;/type&gt; )</div><div>+        &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Returns the sorted, deduplicated union of both inputs.</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyarray_union('{1,2}'::int8[], '{2,3}')&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;{1,2,3}&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyarray_union_elem&lt;/function&gt; ( &lt;type&gt;anyarray&lt;/type&gt;, &lt;type&gt;anyelement&lt;/type&gt; )</div><div>+        &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Adds an element to the array if not already present; result is</div><div>+        sorted and deduplicated.</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyarray_union_elem('{1,2,3}'::int8[], 4::int8)&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;{1,2,3,4}&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyarray_difference&lt;/function&gt; ( &lt;type&gt;anyarray&lt;/type&gt;, &lt;type&gt;anyarray&lt;/type&gt; )</div><div>+        &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Returns the sorted, deduplicated values from the first array that</div><div>+        are not in the second.</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyarray_difference('{1,2,3,4}'::int8[], '{2,4}')&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;{1,3}&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyarray_difference_elem&lt;/function&gt; ( &lt;type&gt;anyarray&lt;/type&gt;, &lt;type&gt;anyelement&lt;/type&gt; )</div><div>+        &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Removes all occurrences of the given element, preserving the</div><div>+        relative order of the surviving elements.</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyarray_difference_elem('{1,2,2,3,2}'::int8[], 2::int8)&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;{1,3}&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+</div><div>+      &lt;row&gt;</div><div>+       &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+        &lt;function&gt;anyquery_querytree&lt;/function&gt; ( &lt;type&gt;anyquery&lt;/type&gt; )</div><div>+        &lt;returnvalue&gt;text&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        Returns the query in postfix form; useful for debugging.</div><div>+       &lt;/para&gt;</div><div>+       &lt;para&gt;</div><div>+        &lt;literal&gt;anyquery_querytree('1 &amp;amp; 2 | 3'::anyquery)&lt;/literal&gt;</div><div>+        &lt;returnvalue&gt;1 2 &amp;amp; 3 |&lt;/returnvalue&gt;</div><div>+       &lt;/para&gt;&lt;/entry&gt;</div><div>+      &lt;/row&gt;</div><div>+     &lt;/tbody&gt;</div><div>+    &lt;/tgroup&gt;</div><div>+   &lt;/table&gt;</div><div>+</div><div>+   &lt;table id="anyarray-op-table"&gt;</div><div>+    &lt;title&gt;&lt;filename&gt;anyarray&lt;/filename&gt; Operators&lt;/title&gt;</div><div>+     &lt;tgroup cols="1"&gt;</div><div>+      &lt;thead&gt;</div><div>+       &lt;row&gt;</div><div>+        &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+         Operator</div><div>+        &lt;/para&gt;</div><div>+        &lt;para&gt;</div><div>+         Description</div><div>+        &lt;/para&gt;&lt;/entry&gt;</div><div>+       &lt;/row&gt;</div><div>+      &lt;/thead&gt;</div><div>+</div><div>+      &lt;tbody&gt;</div><div>+       &lt;row&gt;</div><div>+        &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+         &lt;type&gt;&lt;/type&gt; &lt;literal&gt;#&lt;/literal&gt; &lt;type&gt;anyarray&lt;/type&gt;</div><div>+         &lt;returnvalue&gt;integer&lt;/returnvalue&gt;</div><div>+        &lt;/para&gt;</div><div>+        &lt;para&gt;</div><div>+         Returns the number of elements in the array (prefix operator).</div><div>+        &lt;/para&gt;&lt;/entry&gt;</div><div>+       &lt;/row&gt;</div><div>+</div><div>+       &lt;row&gt;</div><div>+        &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+         &lt;type&gt;anyarray&lt;/type&gt; &lt;literal&gt;#&lt;/literal&gt; &lt;type&gt;anyelement&lt;/type&gt;</div><div>+         &lt;returnvalue&gt;integer&lt;/returnvalue&gt;</div><div>+        &lt;/para&gt;</div><div>+        &lt;para&gt;</div><div>+         Returns the 1-based index of the right operand in the array, or 0</div><div>+         if not found.  Same as &lt;function&gt;anyarray_idx&lt;/function&gt;.</div><div>+        &lt;/para&gt;&lt;/entry&gt;</div><div>+       &lt;/row&gt;</div><div>+</div><div>+       &lt;row&gt;</div><div>+        &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+         &lt;type&gt;anyarray&lt;/type&gt; &lt;literal&gt;&amp;amp;&lt;/literal&gt; &lt;type&gt;anyarray&lt;/type&gt;</div><div>+         &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+        &lt;/para&gt;</div><div>+        &lt;para&gt;</div><div>+         Set intersection of the operands.</div><div>+        &lt;/para&gt;&lt;/entry&gt;</div><div>+       &lt;/row&gt;</div><div>+</div><div>+       &lt;row&gt;</div><div>+        &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+         &lt;type&gt;anyarray&lt;/type&gt; &lt;literal&gt;|&lt;/literal&gt; &lt;type&gt;anyarray&lt;/type&gt;</div><div>+         &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+        &lt;/para&gt;</div><div>+        &lt;para&gt;</div><div>+         Set union of the operands.</div><div>+        &lt;/para&gt;&lt;/entry&gt;</div><div>+       &lt;/row&gt;</div><div>+</div><div>+       &lt;row&gt;</div><div>+        &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+         &lt;type&gt;anyarray&lt;/type&gt; &lt;literal&gt;|&lt;/literal&gt; &lt;type&gt;anyelement&lt;/type&gt;</div><div>+         &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+        &lt;/para&gt;</div><div>+        &lt;para&gt;</div><div>+         Adds the right operand to the array if not already present.</div><div>+        &lt;/para&gt;&lt;/entry&gt;</div><div>+       &lt;/row&gt;</div><div>+</div><div>+       &lt;row&gt;</div><div>+        &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+         &lt;type&gt;anyarray&lt;/type&gt; &lt;literal&gt;-&lt;/literal&gt; &lt;type&gt;anyarray&lt;/type&gt;</div><div>+         &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+        &lt;/para&gt;</div><div>+        &lt;para&gt;</div><div>+         Set difference: elements of the left array not present in the</div><div>+         right.</div><div>+        &lt;/para&gt;&lt;/entry&gt;</div><div>+       &lt;/row&gt;</div><div>+</div><div>+       &lt;row&gt;</div><div>+        &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+         &lt;type&gt;anyarray&lt;/type&gt; &lt;literal&gt;-&lt;/literal&gt; &lt;type&gt;anyelement&lt;/type&gt;</div><div>+         &lt;returnvalue&gt;anyarray&lt;/returnvalue&gt;</div><div>+        &lt;/para&gt;</div><div>+        &lt;para&gt;</div><div>+         Removes all occurrences of the right operand from the array.</div><div>+        &lt;/para&gt;&lt;/entry&gt;</div><div>+       &lt;/row&gt;</div><div>+</div><div>+       &lt;row&gt;</div><div>+        &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+         &lt;type&gt;anyarray&lt;/type&gt; &lt;literal&gt;@@&lt;/literal&gt; &lt;type&gt;anyquery&lt;/type&gt;</div><div>+         &lt;returnvalue&gt;boolean&lt;/returnvalue&gt;</div><div>+        &lt;/para&gt;</div><div>+        &lt;para&gt;</div><div>+         True if the array satisfies the query (see below).</div><div>+        &lt;/para&gt;&lt;/entry&gt;</div><div>+       &lt;/row&gt;</div><div>+</div><div>+       &lt;row&gt;</div><div>+        &lt;entry role="func_table_entry"&gt;&lt;para role="func_signature"&gt;</div><div>+         &lt;type&gt;anyquery&lt;/type&gt; &lt;literal&gt;~~&lt;/literal&gt; &lt;type&gt;anyarray&lt;/type&gt;</div><div>+         &lt;returnvalue&gt;boolean&lt;/returnvalue&gt;</div><div>+        &lt;/para&gt;</div><div>+        &lt;para&gt;</div><div>+         Commutator of &lt;literal&gt;@@&lt;/literal&gt;.</div><div>+        &lt;/para&gt;&lt;/entry&gt;</div><div>+       &lt;/row&gt;</div><div>+      &lt;/tbody&gt;</div><div>+     &lt;/tgroup&gt;</div><div>+   &lt;/table&gt;</div><div>+</div><div>+   &lt;para&gt;</div><div>+    The operators &lt;literal&gt;&amp;amp;&amp;amp;&lt;/literal&gt;, &lt;literal&gt;@&amp;gt;&lt;/literal&gt;,</div><div>+    &lt;literal&gt;&amp;lt;@&lt;/literal&gt; and &lt;literal&gt;=&lt;/literal&gt; on arrays are</div><div>+    &lt;emphasis&gt;not&lt;/emphasis&gt; redefined by this extension; the built-in</div><div>+    PostgreSQL operators are used.</div><div>+   &lt;/para&gt;</div><div>+ &lt;/sect2&gt;</div><div>+</div><div>+ &lt;sect2 id="anyarray-query"&gt;</div><div>+  &lt;title&gt;The &lt;type&gt;anyquery&lt;/type&gt; Type&lt;/title&gt;</div><div>+</div><div>+  &lt;para&gt;</div><div>+   &lt;type&gt;anyquery&lt;/type&gt; represents a boolean expression over array</div><div>+   elements.  Values are textual tokens; the element type used to interpret</div><div>+   them is determined by the left-hand side of &lt;literal&gt;@@&lt;/literal&gt; at</div><div>+   query time.  Tokens are combined with the operators</div><div>+   &lt;literal&gt;&amp;amp;&lt;/literal&gt; (AND), &lt;literal&gt;|&lt;/literal&gt; (OR) and</div><div>+   &lt;literal&gt;!&lt;/literal&gt; (NOT).  Parentheses control precedence; without</div><div>+   them &lt;literal&gt;&amp;amp;&lt;/literal&gt; binds tighter than &lt;literal&gt;|&lt;/literal&gt;.</div><div>+  &lt;/para&gt;</div><div>+</div><div>+  &lt;para&gt;</div><div>+   A token consists of any sequence of characters that are not whitespace</div><div>+   or one of &lt;literal&gt;&amp;amp; | ! ( ) "&lt;/literal&gt;.  To include those</div><div>+   characters or embedded whitespace, wrap the token in double quotes:</div><div>+   &lt;literal&gt;"hello world"&lt;/literal&gt;.  Inside quotes, a backslash escapes</div><div>+   the next character.</div><div>+  &lt;/para&gt;</div><div>+</div><div>+  &lt;para&gt;</div><div>+   At evaluation time, each token is parsed by the input function of the</div><div>+   array's element type.  This means</div><div>+   &lt;literal&gt;'1 &amp;amp; 2'::anyquery @@ ARRAY[1,2,3]::bigint[]&lt;/literal&gt;</div><div>+   parses &lt;quote&gt;1&lt;/quote&gt; and &lt;quote&gt;2&lt;/quote&gt; as &lt;type&gt;bigint&lt;/type&gt;; the</div><div>+   same query against a &lt;type&gt;text[]&lt;/type&gt; column compares the literal</div><div>+   strings &lt;quote&gt;1&lt;/quote&gt; and &lt;quote&gt;2&lt;/quote&gt;.  An unparseable token</div><div>+   raises an error at &lt;literal&gt;@@&lt;/literal&gt;-time, not at parse time.</div><div>+  &lt;/para&gt;</div><div>+ &lt;/sect2&gt;</div><div>+</div><div>+ &lt;sect2 id="anyarray-index"&gt;</div><div>+  &lt;title&gt;Index Support&lt;/title&gt;</div><div>+</div><div>+  &lt;para&gt;</div><div>+   &lt;filename&gt;anyarray&lt;/filename&gt; provides a polymorphic GiST opclass and</div><div>+   per-element-type GIN opclasses.  All of them support the standard array</div><div>+   operators (&lt;literal&gt;&amp;amp;&amp;amp;&lt;/literal&gt;, &lt;literal&gt;@&amp;gt;&lt;/literal&gt;,</div><div>+   &lt;literal&gt;&amp;lt;@&lt;/literal&gt;, &lt;literal&gt;=&lt;/literal&gt;) as well as</div><div>+   &lt;literal&gt;@@&lt;/literal&gt;.</div><div>+  &lt;/para&gt;</div><div>+</div><div>+  &lt;sect3 id="anyarray-index-gist"&gt;</div><div>+   &lt;title&gt;GiST: &lt;literal&gt;anyarray_gist_ops&lt;/literal&gt;&lt;/title&gt;</div><div>+</div><div>+   &lt;para&gt;</div><div>+    A single opclass on &lt;type&gt;anyarray&lt;/type&gt; works for any element type</div><div>+    that has a default hash function.  Each indexed array is summarised as</div><div>+    a fixed-size bit vector by hashing every element into one bit; internal</div><div>+    nodes store the union of their children.  All matches are rechecked</div><div>+    because signatures are lossy.</div><div>+   &lt;/para&gt;</div><div>+</div><div>+   &lt;para&gt;</div><div>+    The optional integer parameter &lt;literal&gt;siglen&lt;/literal&gt; sets the</div><div>+    signature length in bytes.  The default is 252 bytes (2016 bits);</div><div>+    valid values are between 1 and the index-key size limit.  Longer</div><div>+    signatures produce more precise indexes at the cost of more storage.</div><div>+   &lt;/para&gt;</div><div>+</div><div>+   &lt;programlisting&gt;</div><div>+CREATE INDEX my_idx ON my_table USING gist (col anyarray_gist_ops);</div><div>+CREATE INDEX my_idx ON my_table USING gist (col anyarray_gist_ops(siglen=64));</div><div>+   &lt;/programlisting&gt;</div><div>+  &lt;/sect3&gt;</div><div>+</div><div>+  &lt;sect3 id="anyarray-index-gin"&gt;</div><div>+   &lt;title&gt;GIN: per-type opclasses&lt;/title&gt;</div><div>+</div><div>+   &lt;para&gt;</div><div>+    Because the GIN extractQuery API does not expose the indexed column's</div><div>+    element type when the query is an &lt;type&gt;anyquery&lt;/type&gt;, GIN support</div><div>+    is provided as one opclass per concrete element type.  Currently</div><div>+    supplied:</div><div>+   &lt;/para&gt;</div><div>+</div><div>+   &lt;itemizedlist&gt;</div><div>+    &lt;listitem&gt;&lt;para&gt;</div><div>+     &lt;literal&gt;int8_anyquery_gin_ops&lt;/literal&gt; for &lt;type&gt;bigint[]&lt;/type&gt;</div><div>+    &lt;/para&gt;&lt;/listitem&gt;</div><div>+    &lt;listitem&gt;&lt;para&gt;</div><div>+     &lt;literal&gt;uuid_anyquery_gin_ops&lt;/literal&gt; for &lt;type&gt;uuid[]&lt;/type&gt;</div><div>+    &lt;/para&gt;&lt;/listitem&gt;</div><div>+    &lt;listitem&gt;&lt;para&gt;</div><div>+     &lt;literal&gt;text_anyquery_gin_ops&lt;/literal&gt; for &lt;type&gt;text[]&lt;/type&gt;</div><div>+    &lt;/para&gt;&lt;/listitem&gt;</div><div>+   &lt;/itemizedlist&gt;</div><div>+</div><div>+   &lt;para&gt;</div><div>+    These opclasses are drop-in replacements for the built-in</div><div>+    &lt;literal&gt;array_ops&lt;/literal&gt;; they additionally support</div><div>+    &lt;literal&gt;@@&lt;/literal&gt;.</div><div>+   &lt;/para&gt;</div><div>+</div><div>+   &lt;programlisting&gt;</div><div>+CREATE INDEX my_idx ON my_table USING gin (col int8_anyquery_gin_ops);</div><div>+   &lt;/programlisting&gt;</div><div>+</div><div>+   &lt;para&gt;</div><div>+    For element types not in the list above, use the built-in GIN</div><div>+    &lt;literal&gt;array_ops&lt;/literal&gt; for set queries and rely on</div><div>+    &lt;literal&gt;anyarray_gist_ops&lt;/literal&gt; for &lt;literal&gt;@@&lt;/literal&gt;.</div><div>+   &lt;/para&gt;</div><div>+  &lt;/sect3&gt;</div><div>+ &lt;/sect2&gt;</div><div>+</div><div>+ &lt;sect2 id="anyarray-example"&gt;</div><div>+  &lt;title&gt;Example&lt;/title&gt;</div><div>+</div><div>+&lt;programlisting&gt;</div><div>+CREATE EXTENSION anyarray;</div><div>+</div><div>+CREATE TABLE message (mid INT PRIMARY KEY, tags TEXT[]);</div><div>+</div><div>+CREATE INDEX message_tags_gist ON message USING gist (tags anyarray_gist_ops);</div><div>+CREATE INDEX message_tags_gin  ON message USING gin  (tags text_anyquery_gin_ops);</div><div>+</div><div>+-- Messages tagged with both 'urgent' and 'bug':</div><div>+SELECT mid FROM message WHERE tags @&amp;gt; ARRAY['urgent','bug'];</div><div>+</div><div>+-- Same, with the query syntax:</div><div>+SELECT mid FROM message WHERE tags @@ 'urgent &amp;amp; bug'::anyquery;</div><div>+</div><div>+-- Messages tagged 'urgent' but not 'wontfix':</div><div>+SELECT mid FROM message WHERE tags @@ 'urgent &amp;amp; !wontfix'::anyquery;</div><div>+</div><div>+-- Sort and deduplicate a tag list:</div><div>+SELECT anyarray_uniq(tags) FROM message WHERE mid = 42;</div><div>+&lt;/programlisting&gt;</div><div>+ &lt;/sect2&gt;</div><div>+</div><div>+ &lt;sect2 id="anyarray-authors"&gt;</div><div>+  &lt;title&gt;Authors&lt;/title&gt;</div><div>+</div><div>+  &lt;para&gt;</div><div>+   The &lt;filename&gt;anyarray&lt;/filename&gt; module is a generalization of</div><div>+   &lt;xref linkend="intarray"/&gt; by Teodor Sigaev and Oleg Bartunov.</div><div>+  &lt;/para&gt;</div><div>+ &lt;/sect2&gt;</div><div>+</div><div>+&lt;/sect1&gt;</div><div>diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml</div><div>index b9b03654aad..c9aaf79cb69 100644</div><div>--- a/doc/src/sgml/contrib.sgml</div><div>+++ b/doc/src/sgml/contrib.sgml</div><div>@@ -128,6 +128,7 @@ CREATE EXTENSION &lt;replaceable&gt;extension_name&lt;/replaceable&gt;;</div><div>  &lt;/para&gt;</div><div> </div><div>  &amp;amcheck;</div><div>+ &amp;anyarray;</div><div>  &amp;auth-delay;</div><div>  &amp;auto-explain;</div><div>  &amp;basebackup-to-shell;</div><div>diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml</div><div>index 25a85082759..0530342e74b 100644</div><div>--- a/doc/src/sgml/filelist.sgml</div><div>+++ b/doc/src/sgml/filelist.sgml</div><div>@@ -119,6 +119,7 @@</div><div> &lt;!-- contrib information --&gt;</div><div> &lt;!ENTITY contrib         SYSTEM "contrib.sgml"&gt;</div><div> &lt;!ENTITY amcheck         SYSTEM "amcheck.sgml"&gt;</div><div>+&lt;!ENTITY anyarray        SYSTEM "anyarray.sgml"&gt;</div><div> &lt;!ENTITY auth-delay      SYSTEM "auth-delay.sgml"&gt;</div><div> &lt;!ENTITY auto-explain    SYSTEM "auto-explain.sgml"&gt;</div><div> &lt;!ENTITY basic-archive   SYSTEM "basic-archive.sgml"&gt;</div><div>diff --git a/src/test/regress/regression.diffs b/src/test/regress/regression.diffs</div><div>new file mode 100644</div><div>index 00000000000..e69de29bb2d</div><div>diff --git a/src/test/regress/regression.out b/src/test/regress/regression.out</div><div>new file mode 100644</div><div>index 00000000000..9d8d484179c</div><div>--- /dev/null</div><div>+++ b/src/test/regress/regression.out</div><div>@@ -0,0 +1,2 @@</div><div>+# using postmaster on Unix socket, default port</div><div>+# command failed: "/Users/vlad/pgsql/bin/psql" -X -q -c "SET client_min_messages = warning" -c "DROP DATABASE IF EXISTS \"regression\"" "postgres"Bail out!</div><div>diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list</div><div>index cbd9e10fc1d..049485f5c24 100644</div><div>--- a/src/tools/pgindent/typedefs.list</div><div>+++ b/src/tools/pgindent/typedefs.list</div><div>@@ -126,7 +126,13 @@ AnalyzeAttrFetchFunc</div><div> AnalyzeForeignTable_function</div><div> AnlExprData</div><div> AnlIndexData</div><div>+AnyArrayGistKey</div><div>+AnyArrayGistOptions</div><div> AnyArrayType</div><div>+AnyArrayTypeInfo</div><div>+AnyQuery</div><div>+AnyQueryCheck</div><div>+AnyQueryItem</div><div> Append</div><div> AppendPath</div><div> AppendPathInput</div><div>@@ -1744,6 +1750,7 @@ MVNDistinct</div><div> MVNDistinctItem</div><div> ManyTestResource</div><div> ManyTestResourceKind</div><div>+MatchContext</div><div> Material</div><div> MaterialPath</div><div> MaterialState</div><div>@@ -2372,6 +2379,7 @@ PgXmlStrictness</div><div> Pg_abi_values</div><div> Pg_finfo_record</div><div> Pg_magic_struct</div><div>+PickSplitEntry</div><div> PipeProtoChunk</div><div> PipeProtoHeader</div><div> PlaceHolderInfo</div><div>-- </div><div>2.39.5 (Apple Git-154)</div><div> </div></div></div>

Attachment Content-Type Size
unknown_filename text/html 216.8 KB

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Jelte Fennema-Nio 2026-05-18 10:23:20 Re: Support LIKE with nondeterministic collations
Previous Message Vlad Lesin 2026-05-18 10:11:56 Re: [PATCH] Fix ProcKill lock-group vs procLatch recycle race