From 322cf03dbb7aaf61109f89b44e03e6e9a8d6b3f8 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Wed, 20 Mar 2024 11:51:53 -0400
Subject: [PATCH v1 3/4] docs: Merge separate chapters on built-in index AMs
 into one.

The documetation index is getting very long, which makes it hard
to find things. Since these chapters are all very similar in structure
and content, merging them is a natural way of reducing the size of
the toplevel index.
---
 doc/src/sgml/brin.sgml       | 1344 --------
 doc/src/sgml/btree.sgml      |  914 ------
 doc/src/sgml/filelist.sgml   |    7 +-
 doc/src/sgml/gin.sgml        |  714 -----
 doc/src/sgml/gist.sgml       | 1387 ---------
 doc/src/sgml/hash.sgml       |  162 -
 doc/src/sgml/indextypes.sgml | 5597 ++++++++++++++++++++++++++++++++++
 doc/src/sgml/postgres.sgml   |    7 +-
 doc/src/sgml/spgist.sgml     | 1076 -------
 9 files changed, 5599 insertions(+), 5609 deletions(-)
 delete mode 100644 doc/src/sgml/brin.sgml
 delete mode 100644 doc/src/sgml/btree.sgml
 delete mode 100644 doc/src/sgml/gin.sgml
 delete mode 100644 doc/src/sgml/gist.sgml
 delete mode 100644 doc/src/sgml/hash.sgml
 create mode 100644 doc/src/sgml/indextypes.sgml
 delete mode 100644 doc/src/sgml/spgist.sgml

diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml
deleted file mode 100644
index d898cc4720..0000000000
--- a/doc/src/sgml/brin.sgml
+++ /dev/null
@@ -1,1344 +0,0 @@
-<!-- doc/src/sgml/brin.sgml -->
-
-<chapter id="brin">
-<title>BRIN Indexes</title>
-
-   <indexterm>
-    <primary>index</primary>
-    <secondary>BRIN</secondary>
-   </indexterm>
-
-<sect1 id="brin-intro">
- <title>Introduction</title>
-
- <para>
-  <acronym>BRIN</acronym> stands for Block Range Index.
-  <acronym>BRIN</acronym> is designed for handling very large tables
-  in which certain columns have some natural correlation with their
-  physical location within the table.
- </para>
-
- <para>
-  <acronym>BRIN</acronym> works in terms of <firstterm>block ranges</firstterm>
-  (or <quote>page ranges</quote>).
-  A block range is a group of pages that are physically
-  adjacent in the table; for each block range, some summary info is stored
-  by the index.
-  For example, a table storing a store's sale orders might have
-  a date column on which each order was placed, and most of the time
-  the entries for earlier orders will appear earlier in the table as well;
-  a table storing a ZIP code column might have all codes for a city
-  grouped together naturally.
- </para>
-
- <para>
-  <acronym>BRIN</acronym> indexes can satisfy queries via regular bitmap
-  index scans, and will return all tuples in all pages within each range if
-  the summary info stored by the index is <firstterm>consistent</firstterm> with the
-  query conditions.
-  The query executor is in charge of rechecking these tuples and discarding
-  those that do not match the query conditions &mdash; in other words, these
-  indexes are lossy.
-  Because a <acronym>BRIN</acronym> index is very small, scanning the index
-  adds little overhead compared to a sequential scan, but may avoid scanning
-  large parts of the table that are known not to contain matching tuples.
- </para>
-
- <para>
-  The specific data that a <acronym>BRIN</acronym> index will store,
-  as well as the specific queries that the index will be able to satisfy,
-  depend on the operator class selected for each column of the index.
-  Data types having a linear sort order can have operator classes that
-  store the minimum and maximum value within each block range, for instance;
-  geometrical types might store the bounding box for all the objects
-  in the block range.
- </para>
-
- <para>
-  The size of the block range is determined at index creation time by
-  the <literal>pages_per_range</literal> storage parameter.  The number of index
-  entries will be equal to the size of the relation in pages divided by
-  the selected value for <literal>pages_per_range</literal>.  Therefore, the smaller
-  the number, the larger the index becomes (because of the need to
-  store more index entries), but at the same time the summary data stored can
-  be more precise and more data blocks can be skipped during an index scan.
- </para>
-
- <sect2 id="brin-operation">
-  <title>Index Maintenance</title>
-
-  <para>
-   At the time of creation, all existing heap pages are scanned and a
-   summary index tuple is created for each range, including the
-   possibly-incomplete range at the end.
-   As new pages are filled with data, page ranges that are already
-   summarized will cause the summary information to be updated with data
-   from the new tuples.
-   When a new page is created that does not fall within the last
-   summarized range, the range that the new page belongs to
-   does not automatically acquire a summary tuple;
-   those tuples remain unsummarized until a summarization run is
-   invoked later, creating the initial summary for that range.
-  </para>
-
-  <para>
-   There are several ways to trigger the initial summarization of a page range.
-   If the table is vacuumed, either manually or by
-   <link linkend="autovacuum">autovacuum</link>, all existing unsummarized
-   page ranges are summarized.
-   Also, if the index's
-   <xref linkend="index-reloption-autosummarize"/> parameter is enabled,
-   which it isn't by default,
-   whenever autovacuum runs in that database, summarization will occur for all
-   unsummarized page ranges that have been filled,
-   regardless of whether the table itself is processed by autovacuum; see below.
-  </para>
-
-  <para>
-   Lastly, the following functions can be used (while these functions run,
-   <xref linkend="guc-search-path"/> is temporarily changed to
-   <literal>pg_catalog, pg_temp</literal>):
-   <simplelist>
-    <member>
-     <function>brin_summarize_new_values(regclass)</function>
-     which summarizes all unsummarized ranges;
-    </member>
-    <member>
-     <function>brin_summarize_range(regclass, bigint)</function>
-     which summarizes only the range containing the given page,
-     if it is unsummarized.
-    </member>
-   </simplelist>
-  </para>
-
-  <para>
-   When autosummarization is enabled, a request is sent to
-   <literal>autovacuum</literal> to execute a targeted summarization
-   for a block range when an insertion is detected for the first item
-   of the first page of the next block range,
-   to be fulfilled the next time an autovacuum
-   worker finishes running in the
-   same database.  If the request queue is full, the request is not recorded
-   and a message is sent to the server log:
-<screen>
-LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was not recorded
-</screen>
-   When this happens, the range will remain unsummarized until the next
-   regular vacuum run on the table, or one of the functions mentioned above
-   are invoked.
-  </para>
-
-  <para>
-   Conversely, a range can be de-summarized using the
-   <function>brin_desummarize_range(regclass, bigint)</function> function,
-   which is useful when the index tuple is no longer a very good
-   representation because the existing values have changed.
-   See <xref linkend="functions-admin-index"/> for details.
-  </para>
-
- </sect2>
-</sect1>
-
-<sect1 id="brin-builtin-opclasses">
- <title>Built-in Operator Classes</title>
-
- <para>
-  The core <productname>PostgreSQL</productname> distribution
-  includes the <acronym>BRIN</acronym> operator classes shown in
-  <xref linkend="brin-builtin-opclasses-table"/>.
- </para>
-
- <para>
-  The <firstterm>minmax</firstterm>
-  operator classes store the minimum and the maximum values appearing
-  in the indexed column within the range.  The <firstterm>inclusion</firstterm>
-  operator classes store a value which includes the values in the indexed
-  column within the range.  The <firstterm>bloom</firstterm> operator
-  classes build a Bloom filter for all values in the range.  The
-  <firstterm>minmax-multi</firstterm> operator classes store multiple
-  minimum and maximum values, representing values appearing in the indexed
-  column within the range.
- </para>
-
- <table id="brin-builtin-opclasses-table">
-  <title>Built-in <acronym>BRIN</acronym> Operator Classes</title>
-  <tgroup cols="2">
-   <thead>
-    <row>
-     <entry>Name</entry>
-     <entry>Indexable Operators</entry>
-    </row>
-   </thead>
-   <tbody>
-    <row>
-     <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry>
-     <entry><literal>= (bit,bit)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (bit,bit)</literal></entry></row>
-    <row><entry><literal>&gt; (bit,bit)</literal></entry></row>
-    <row><entry><literal>&lt;= (bit,bit)</literal></entry></row>
-    <row><entry><literal>&gt;= (bit,bit)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="12"><literal>box_inclusion_ops</literal></entry>
-     <entry><literal>@&gt; (box,point)</literal></entry>
-    </row>
-    <row><entry><literal>&lt;&lt; (box,box)</literal></entry></row>
-    <row><entry><literal>&amp;&lt; (box,box)</literal></entry></row>
-    <row><entry><literal>&amp;&gt; (box,box)</literal></entry></row>
-    <row><entry><literal>&gt;&gt; (box,box)</literal></entry></row>
-    <row><entry><literal>&lt;@ (box,box)</literal></entry></row>
-    <row><entry><literal>@&gt; (box,box)</literal></entry></row>
-    <row><entry><literal>~= (box,box)</literal></entry></row>
-    <row><entry><literal>&amp;&amp; (box,box)</literal></entry></row>
-    <row><entry><literal>&lt;&lt;| (box,box)</literal></entry></row>
-    <row><entry><literal>&amp;&lt;| (box,box)</literal></entry></row>
-    <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row>
-    <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry>
-     <entry><literal>= (character,character)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry>
-     <entry><literal>= (character,character)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (character,character)</literal></entry></row>
-    <row><entry><literal>&lt;= (character,character)</literal></entry></row>
-    <row><entry><literal>&gt; (character,character)</literal></entry></row>
-    <row><entry><literal>&gt;= (character,character)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>bytea_bloom_ops</literal></entry>
-     <entry><literal>= (bytea,bytea)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry>
-     <entry><literal>= (bytea,bytea)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (bytea,bytea)</literal></entry></row>
-    <row><entry><literal>&lt;= (bytea,bytea)</literal></entry></row>
-    <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row>
-    <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>char_bloom_ops</literal></entry>
-     <entry><literal>= ("char","char")</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry>
-     <entry><literal>= ("char","char")</literal></entry>
-    </row>
-    <row><entry><literal>&lt; ("char","char")</literal></entry></row>
-    <row><entry><literal>&lt;= ("char","char")</literal></entry></row>
-    <row><entry><literal>&gt; ("char","char")</literal></entry></row>
-    <row><entry><literal>&gt;= ("char","char")</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>date_bloom_ops</literal></entry>
-     <entry><literal>= (date,date)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry>
-     <entry><literal>= (date,date)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (date,date)</literal></entry></row>
-    <row><entry><literal>&lt;= (date,date)</literal></entry></row>
-    <row><entry><literal>&gt; (date,date)</literal></entry></row>
-    <row><entry><literal>&gt;= (date,date)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry>
-     <entry><literal>= (date,date)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (date,date)</literal></entry></row>
-    <row><entry><literal>&lt;= (date,date)</literal></entry></row>
-    <row><entry><literal>&gt; (date,date)</literal></entry></row>
-    <row><entry><literal>&gt;= (date,date)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>float4_bloom_ops</literal></entry>
-     <entry><literal>= (float4,float4)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry>
-     <entry><literal>= (float4,float4)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (float4,float4)</literal></entry></row>
-    <row><entry><literal>&gt; (float4,float4)</literal></entry></row>
-    <row><entry><literal>&lt;= (float4,float4)</literal></entry></row>
-    <row><entry><literal>&gt;= (float4,float4)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry>
-     <entry><literal>= (float4,float4)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (float4,float4)</literal></entry></row>
-    <row><entry><literal>&gt; (float4,float4)</literal></entry></row>
-    <row><entry><literal>&lt;= (float4,float4)</literal></entry></row>
-    <row><entry><literal>&gt;= (float4,float4)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>float8_bloom_ops</literal></entry>
-     <entry><literal>= (float8,float8)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry>
-     <entry><literal>= (float8,float8)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (float8,float8)</literal></entry></row>
-    <row><entry><literal>&lt;= (float8,float8)</literal></entry></row>
-    <row><entry><literal>&gt; (float8,float8)</literal></entry></row>
-    <row><entry><literal>&gt;= (float8,float8)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry>
-     <entry><literal>= (float8,float8)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (float8,float8)</literal></entry></row>
-    <row><entry><literal>&lt;= (float8,float8)</literal></entry></row>
-    <row><entry><literal>&gt; (float8,float8)</literal></entry></row>
-    <row><entry><literal>&gt;= (float8,float8)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry>
-     <entry><literal>&lt;&lt; (inet,inet)</literal></entry>
-    </row>
-    <row><entry><literal>&lt;&lt;= (inet,inet)</literal></entry></row>
-    <row><entry><literal>&gt;&gt; (inet,inet)</literal></entry></row>
-    <row><entry><literal>&gt;&gt;= (inet,inet)</literal></entry></row>
-    <row><entry><literal>= (inet,inet)</literal></entry></row>
-    <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>inet_bloom_ops</literal></entry>
-     <entry><literal>= (inet,inet)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry>
-     <entry><literal>= (inet,inet)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (inet,inet)</literal></entry></row>
-    <row><entry><literal>&lt;= (inet,inet)</literal></entry></row>
-    <row><entry><literal>&gt; (inet,inet)</literal></entry></row>
-    <row><entry><literal>&gt;= (inet,inet)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry>
-     <entry><literal>= (inet,inet)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (inet,inet)</literal></entry></row>
-    <row><entry><literal>&lt;= (inet,inet)</literal></entry></row>
-    <row><entry><literal>&gt; (inet,inet)</literal></entry></row>
-    <row><entry><literal>&gt;= (inet,inet)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>int2_bloom_ops</literal></entry>
-     <entry><literal>= (int2,int2)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry>
-     <entry><literal>= (int2,int2)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (int2,int2)</literal></entry></row>
-    <row><entry><literal>&gt; (int2,int2)</literal></entry></row>
-    <row><entry><literal>&lt;= (int2,int2)</literal></entry></row>
-    <row><entry><literal>&gt;= (int2,int2)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry>
-     <entry><literal>= (int2,int2)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (int2,int2)</literal></entry></row>
-    <row><entry><literal>&gt; (int2,int2)</literal></entry></row>
-    <row><entry><literal>&lt;= (int2,int2)</literal></entry></row>
-    <row><entry><literal>&gt;= (int2,int2)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>int4_bloom_ops</literal></entry>
-     <entry><literal>= (int4,int4)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry>
-     <entry><literal>= (int4,int4)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (int4,int4)</literal></entry></row>
-    <row><entry><literal>&gt; (int4,int4)</literal></entry></row>
-    <row><entry><literal>&lt;= (int4,int4)</literal></entry></row>
-    <row><entry><literal>&gt;= (int4,int4)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry>
-     <entry><literal>= (int4,int4)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (int4,int4)</literal></entry></row>
-    <row><entry><literal>&gt; (int4,int4)</literal></entry></row>
-    <row><entry><literal>&lt;= (int4,int4)</literal></entry></row>
-    <row><entry><literal>&gt;= (int4,int4)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>int8_bloom_ops</literal></entry>
-     <entry><literal>= (bigint,bigint)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry>
-     <entry><literal>= (bigint,bigint)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row>
-    <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row>
-    <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row>
-    <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry>
-     <entry><literal>= (bigint,bigint)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row>
-    <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row>
-    <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row>
-    <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>interval_bloom_ops</literal></entry>
-     <entry><literal>= (interval,interval)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry>
-     <entry><literal>= (interval,interval)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (interval,interval)</literal></entry></row>
-    <row><entry><literal>&lt;= (interval,interval)</literal></entry></row>
-    <row><entry><literal>&gt; (interval,interval)</literal></entry></row>
-    <row><entry><literal>&gt;= (interval,interval)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry>
-     <entry><literal>= (interval,interval)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (interval,interval)</literal></entry></row>
-    <row><entry><literal>&lt;= (interval,interval)</literal></entry></row>
-    <row><entry><literal>&gt; (interval,interval)</literal></entry></row>
-    <row><entry><literal>&gt;= (interval,interval)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry>
-     <entry><literal>= (macaddr,macaddr)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry>
-     <entry><literal>= (macaddr,macaddr)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row>
-    <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row>
-    <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row>
-    <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry>
-     <entry><literal>= (macaddr,macaddr)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row>
-    <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row>
-    <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row>
-    <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry>
-     <entry><literal>= (macaddr8,macaddr8)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry>
-     <entry><literal>= (macaddr8,macaddr8)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row>
-    <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row>
-    <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row>
-    <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry>
-     <entry><literal>= (macaddr8,macaddr8)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row>
-    <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row>
-    <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row>
-    <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>name_bloom_ops</literal></entry>
-     <entry><literal>= (name,name)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry>
-     <entry><literal>= (name,name)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (name,name)</literal></entry></row>
-    <row><entry><literal>&lt;= (name,name)</literal></entry></row>
-    <row><entry><literal>&gt; (name,name)</literal></entry></row>
-    <row><entry><literal>&gt;= (name,name)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>numeric_bloom_ops</literal></entry>
-     <entry><literal>= (numeric,numeric)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry>
-     <entry><literal>= (numeric,numeric)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row>
-    <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row>
-    <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row>
-    <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry>
-     <entry><literal>= (numeric,numeric)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row>
-    <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row>
-    <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row>
-    <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>oid_bloom_ops</literal></entry>
-     <entry><literal>= (oid,oid)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry>
-     <entry><literal>= (oid,oid)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (oid,oid)</literal></entry></row>
-    <row><entry><literal>&gt; (oid,oid)</literal></entry></row>
-    <row><entry><literal>&lt;= (oid,oid)</literal></entry></row>
-    <row><entry><literal>&gt;= (oid,oid)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry>
-     <entry><literal>= (oid,oid)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (oid,oid)</literal></entry></row>
-    <row><entry><literal>&gt; (oid,oid)</literal></entry></row>
-    <row><entry><literal>&lt;= (oid,oid)</literal></entry></row>
-    <row><entry><literal>&gt;= (oid,oid)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry>
-     <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry>
-     <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row>
-    <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row>
-    <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row>
-    <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry>
-     <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row>
-    <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row>
-    <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row>
-    <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry>
-     <entry><literal>= (anyrange,anyrange)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>&lt;= (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>&gt;= (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>&gt; (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>&amp;&amp; (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>@&gt; (anyrange,anyelement)</literal></entry></row>
-    <row><entry><literal>@&gt; (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>&lt;@ (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>&lt;&lt; (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>&gt;&gt; (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>&amp;&lt; (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row>
-    <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>text_bloom_ops</literal></entry>
-     <entry><literal>= (text,text)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry>
-     <entry><literal>= (text,text)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (text,text)</literal></entry></row>
-    <row><entry><literal>&lt;= (text,text)</literal></entry></row>
-    <row><entry><literal>&gt; (text,text)</literal></entry></row>
-    <row><entry><literal>&gt;= (text,text)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>tid_bloom_ops</literal></entry>
-     <entry><literal>= (tid,tid)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry>
-     <entry><literal>= (tid,tid)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (tid,tid)</literal></entry></row>
-    <row><entry><literal>&gt; (tid,tid)</literal></entry></row>
-    <row><entry><literal>&lt;= (tid,tid)</literal></entry></row>
-    <row><entry><literal>&gt;= (tid,tid)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry>
-     <entry><literal>= (tid,tid)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (tid,tid)</literal></entry></row>
-    <row><entry><literal>&gt; (tid,tid)</literal></entry></row>
-    <row><entry><literal>&lt;= (tid,tid)</literal></entry></row>
-    <row><entry><literal>&gt;= (tid,tid)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry>
-     <entry><literal>= (timestamp,timestamp)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry>
-     <entry><literal>= (timestamp,timestamp)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row>
-    <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row>
-    <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row>
-    <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry>
-     <entry><literal>= (timestamp,timestamp)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row>
-    <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row>
-    <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row>
-    <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry>
-     <entry><literal>= (timestamptz,timestamptz)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry>
-     <entry><literal>= (timestamptz,timestamptz)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row>
-    <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row>
-    <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row>
-    <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry>
-     <entry><literal>= (timestamptz,timestamptz)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row>
-    <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row>
-    <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row>
-    <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>time_bloom_ops</literal></entry>
-     <entry><literal>= (time,time)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry>
-     <entry><literal>= (time,time)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (time,time)</literal></entry></row>
-    <row><entry><literal>&lt;= (time,time)</literal></entry></row>
-    <row><entry><literal>&gt; (time,time)</literal></entry></row>
-    <row><entry><literal>&gt;= (time,time)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry>
-     <entry><literal>= (time,time)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (time,time)</literal></entry></row>
-    <row><entry><literal>&lt;= (time,time)</literal></entry></row>
-    <row><entry><literal>&gt; (time,time)</literal></entry></row>
-    <row><entry><literal>&gt;= (time,time)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>timetz_bloom_ops</literal></entry>
-     <entry><literal>= (timetz,timetz)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry>
-     <entry><literal>= (timetz,timetz)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row>
-    <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row>
-    <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row>
-    <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry>
-     <entry><literal>= (timetz,timetz)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row>
-    <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row>
-    <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row>
-    <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row>
-
-    <row>
-     <entry valign="middle"><literal>uuid_bloom_ops</literal></entry>
-     <entry><literal>= (uuid,uuid)</literal></entry>
-    </row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry>
-     <entry><literal>= (uuid,uuid)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row>
-    <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row>
-    <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row>
-    <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry>
-     <entry><literal>= (uuid,uuid)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row>
-    <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row>
-    <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row>
-    <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row>
-
-    <row>
-     <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry>
-     <entry><literal>= (varbit,varbit)</literal></entry>
-    </row>
-    <row><entry><literal>&lt; (varbit,varbit)</literal></entry></row>
-    <row><entry><literal>&gt; (varbit,varbit)</literal></entry></row>
-    <row><entry><literal>&lt;= (varbit,varbit)</literal></entry></row>
-    <row><entry><literal>&gt;= (varbit,varbit)</literal></entry></row>
-   </tbody>
-  </tgroup>
- </table>
-
-  <sect2 id="brin-builtin-opclasses--parameters">
-   <title>Operator Class Parameters</title>
-
-   <para>
-    Some of the built-in operator classes allow specifying parameters affecting
-    behavior of the operator class.  Each operator class has its own set of
-    allowed parameters.  Only the <literal>bloom</literal> and <literal>minmax-multi</literal>
-    operator classes allow specifying parameters:
-   </para>
-
-   <para>
-    bloom operator classes accept these parameters:
-   </para>
-
-   <variablelist>
-   <varlistentry>
-    <term><literal>n_distinct_per_range</literal></term>
-    <listitem>
-    <para>
-     Defines the estimated number of distinct non-null values in the block
-     range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the
-     Bloom filter. It behaves similarly to <literal>n_distinct</literal> option
-     for <xref linkend="sql-altertable"/>. When set to a positive value,
-     each block range is assumed to contain this number of distinct non-null
-     values. When set to a negative value, which must be greater than or
-     equal to -1, the number of distinct non-null values is assumed to grow linearly with
-     the maximum possible number of tuples in the block range (about 290
-     rows per block). The default value is <literal>-0.1</literal>, and
-     the minimum number of distinct non-null values is <literal>16</literal>.
-    </para>
-    </listitem>
-   </varlistentry>
-
-   <varlistentry>
-    <term><literal>false_positive_rate</literal></term>
-    <listitem>
-    <para>
-     Defines the desired false positive rate used by <acronym>BRIN</acronym>
-     bloom indexes for sizing of the Bloom filter. The values must be
-     between 0.0001 and 0.25. The default value is 0.01, which is 1% false
-     positive rate.
-    </para>
-    </listitem>
-   </varlistentry>
-
-   </variablelist>
-
-   <para>
-    minmax-multi operator classes accept these parameters:
-   </para>
-
-   <variablelist>
-   <varlistentry>
-    <term><literal>values_per_range</literal></term>
-    <listitem>
-    <para>
-     Defines the maximum number of values stored by <acronym>BRIN</acronym>
-     minmax indexes to summarize a block range. Each value may represent
-     either a point, or a boundary of an interval. Values must be between
-     8 and 256, and the default value is 32.
-    </para>
-    </listitem>
-   </varlistentry>
-
-   </variablelist>
-  </sect2>
-
-</sect1>
-
-<sect1 id="brin-extensibility">
- <title>Extensibility</title>
-
- <para>
-  The <acronym>BRIN</acronym> interface has a high level of abstraction,
-  requiring the access method implementer only to implement the semantics
-  of the data type being accessed.  The <acronym>BRIN</acronym> layer
-  itself takes care of concurrency, logging and searching the index structure.
- </para>
-
- <para>
-  All it takes to get a <acronym>BRIN</acronym> access method working is to
-  implement a few user-defined methods, which define the behavior of
-  summary values stored in the index and the way they interact with
-  scan keys.
-  In short, <acronym>BRIN</acronym> combines
-  extensibility with generality, code reuse, and a clean interface.
- </para>
-
- <para>
-  There are four methods that an operator class for <acronym>BRIN</acronym>
-  must provide:
-
-  <variablelist>
-   <varlistentry>
-    <term><function>BrinOpcInfo *opcInfo(Oid type_oid)</function></term>
-    <listitem>
-     <para>
-      Returns internal information about the indexed columns' summary data.
-      The return value must point to a palloc'd <structname>BrinOpcInfo</structname>,
-      which has this definition:
-<programlisting>
-typedef struct BrinOpcInfo
-{
-    /* Number of columns stored in an index column of this opclass */
-    uint16      oi_nstored;
-
-    /* Opaque pointer for the opclass' private use */
-    void       *oi_opaque;
-
-    /* Type cache entries of the stored columns */
-    TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER];
-} BrinOpcInfo;
-</programlisting>
-      <structname>BrinOpcInfo</structname>.<structfield>oi_opaque</structfield> can be used by the
-      operator class routines to pass information between support functions
-      during an index scan.
-     </para>
-    </listitem>
-   </varlistentry>
-
-   <varlistentry>
-    <term><function>bool consistent(BrinDesc *bdesc, BrinValues *column,
-       ScanKey *keys, int nkeys)</function></term>
-    <listitem>
-     <para>
-      Returns whether all the ScanKey entries are consistent with the given
-      indexed values for a range.
-      The attribute number to use is passed as part of the scan key.
-      Multiple scan keys for the same attribute may be passed at once; the
-      number of entries is determined by the <literal>nkeys</literal> parameter.
-     </para>
-    </listitem>
-   </varlistentry>
-
-   <varlistentry>
-    <term><function>bool consistent(BrinDesc *bdesc, BrinValues *column,
-       ScanKey key)</function></term>
-    <listitem>
-     <para>
-      Returns whether the ScanKey is consistent with the given indexed
-      values for a range.
-      The attribute number to use is passed as part of the scan key.
-      This is an older backward-compatible variant of the consistent function.
-     </para>
-    </listitem>
-   </varlistentry>
-
-   <varlistentry>
-    <term><function>bool addValue(BrinDesc *bdesc, BrinValues *column,
-       Datum newval, bool isnull)</function></term>
-    <listitem>
-     <para>
-      Given an index tuple and an indexed value, modifies the indicated
-      attribute of the tuple so that it additionally represents the new value.
-      If any modification was done to the tuple, <literal>true</literal> is
-      returned.
-     </para>
-    </listitem>
-   </varlistentry>
-
-   <varlistentry>
-    <term><function>bool unionTuples(BrinDesc *bdesc, BrinValues *a,
-       BrinValues *b)</function></term>
-    <listitem>
-     <para>
-      Consolidates two index tuples. Given two index tuples, modifies the
-      indicated attribute of the first of them so that it represents both tuples.
-      The second tuple is not modified.
-     </para>
-    </listitem>
-   </varlistentry>
-  </variablelist>
-
-  An operator class for <acronym>BRIN</acronym> can optionally specify the
-  following method:
-
-  <variablelist>
-    <varlistentry>
-     <term><function>void options(local_relopts *relopts)</function></term>
-     <listitem>
-      <para>
-       Defines a set of user-visible parameters that control operator class
-       behavior.
-      </para>
-
-      <para>
-       The <function>options</function> function is passed a pointer to a
-       <structname>local_relopts</structname> struct, which needs to be
-       filled with a set of operator class specific options.  The options
-       can be accessed from other support functions using the
-       <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
-       <literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
-      </para>
-
-      <para>
-       Since both key extraction of indexed values and representation of the
-       key in <acronym>BRIN</acronym> are flexible, they may depend on
-       user-specified parameters.
-      </para>
-     </listitem>
-    </varlistentry>
-  </variablelist>
-
-  The core distribution includes support for four types of operator classes:
-  minmax, minmax-multi, inclusion and bloom.  Operator class definitions
-  using them are shipped for in-core data types as appropriate.  Additional
-  operator classes can be defined by the user for other data types using
-  equivalent definitions, without having to write any source code;
-  appropriate catalog entries being declared is enough.  Note that
-  assumptions about the semantics of operator strategies are embedded in the
-  support functions' source code.
- </para>
-
- <para>
-  Operator classes that implement completely different semantics are also
-  possible, provided implementations of the four main support functions
-  described above are written.  Note that backwards compatibility across major
-  releases is not guaranteed: for example, additional support functions might
-  be required in later releases.
- </para>
-
- <para>
-  To write an operator class for a data type that implements a totally
-  ordered set, it is possible to use the minmax support functions
-  alongside the corresponding operators, as shown in
-  <xref linkend="brin-extensibility-minmax-table"/>.
-  All operator class members (functions and operators) are mandatory.
- </para>
-
- <table id="brin-extensibility-minmax-table">
-  <title>Function and Support Numbers for Minmax Operator Classes</title>
-  <tgroup cols="2">
-   <colspec colname="col1" colwidth="1*"/>
-   <colspec colname="col2" colwidth="2*"/>
-   <thead>
-    <row>
-     <entry>Operator class member</entry>
-     <entry>Object</entry>
-    </row>
-   </thead>
-   <tbody>
-    <row>
-     <entry>Support Function 1</entry>
-     <entry>internal function <function>brin_minmax_opcinfo()</function></entry>
-    </row>
-    <row>
-     <entry>Support Function 2</entry>
-     <entry>internal function <function>brin_minmax_add_value()</function></entry>
-    </row>
-    <row>
-     <entry>Support Function 3</entry>
-     <entry>internal function <function>brin_minmax_consistent()</function></entry>
-    </row>
-    <row>
-     <entry>Support Function 4</entry>
-     <entry>internal function <function>brin_minmax_union()</function></entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 1</entry>
-     <entry>operator less-than</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 2</entry>
-     <entry>operator less-than-or-equal-to</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 3</entry>
-     <entry>operator equal-to</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 4</entry>
-     <entry>operator greater-than-or-equal-to</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 5</entry>
-     <entry>operator greater-than</entry>
-    </row>
-   </tbody>
-  </tgroup>
- </table>
-
- <para>
-  To write an operator class for a complex data type which has values
-  included within another type, it's possible to use the inclusion support
-  functions alongside the corresponding operators, as shown
-  in <xref linkend="brin-extensibility-inclusion-table"/>.  It requires
-  only a single additional function, which can be written in any language.
-  More functions can be defined for additional functionality.  All operators
-  are optional.  Some operators require other operators, as shown as
-  dependencies on the table.
- </para>
-
- <table id="brin-extensibility-inclusion-table">
-  <title>Function and Support Numbers for Inclusion Operator Classes</title>
-  <tgroup cols="3">
-   <colspec colname="col1" colwidth="1*"/>
-   <colspec colname="col2" colwidth="2*"/>
-   <colspec colname="col3" colwidth="1*"/>
-   <thead>
-    <row>
-     <entry>Operator class member</entry>
-     <entry>Object</entry>
-     <entry>Dependency</entry>
-    </row>
-   </thead>
-   <tbody>
-    <row>
-     <entry>Support Function 1</entry>
-     <entry>internal function <function>brin_inclusion_opcinfo()</function></entry>
-     <entry></entry>
-    </row>
-    <row>
-     <entry>Support Function 2</entry>
-     <entry>internal function <function>brin_inclusion_add_value()</function></entry>
-     <entry></entry>
-    </row>
-    <row>
-     <entry>Support Function 3</entry>
-     <entry>internal function <function>brin_inclusion_consistent()</function></entry>
-     <entry></entry>
-    </row>
-    <row>
-     <entry>Support Function 4</entry>
-     <entry>internal function <function>brin_inclusion_union()</function></entry>
-     <entry></entry>
-    </row>
-    <row>
-     <entry>Support Function 11</entry>
-     <entry>function to merge two elements</entry>
-     <entry></entry>
-    </row>
-    <row>
-     <entry>Support Function 12</entry>
-     <entry>optional function to check whether two elements are mergeable</entry>
-     <entry></entry>
-    </row>
-    <row>
-     <entry>Support Function 13</entry>
-     <entry>optional function to check if an element is contained within another</entry>
-     <entry></entry>
-    </row>
-    <row>
-     <entry>Support Function 14</entry>
-     <entry>optional function to check whether an element is empty</entry>
-     <entry></entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 1</entry>
-     <entry>operator left-of</entry>
-     <entry>Operator Strategy 4</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 2</entry>
-     <entry>operator does-not-extend-to-the-right-of</entry>
-     <entry>Operator Strategy 5</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 3</entry>
-     <entry>operator overlaps</entry>
-     <entry></entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 4</entry>
-     <entry>operator does-not-extend-to-the-left-of</entry>
-     <entry>Operator Strategy 1</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 5</entry>
-     <entry>operator right-of</entry>
-     <entry>Operator Strategy 2</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 6, 18</entry>
-     <entry>operator same-as-or-equal-to</entry>
-     <entry>Operator Strategy 7</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 7, 16, 24, 25</entry>
-     <entry>operator contains-or-equal-to</entry>
-     <entry></entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 8, 26, 27</entry>
-     <entry>operator is-contained-by-or-equal-to</entry>
-     <entry>Operator Strategy 3</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 9</entry>
-     <entry>operator does-not-extend-above</entry>
-     <entry>Operator Strategy 11</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 10</entry>
-     <entry>operator is-below</entry>
-     <entry>Operator Strategy 12</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 11</entry>
-     <entry>operator is-above</entry>
-     <entry>Operator Strategy 9</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 12</entry>
-     <entry>operator does-not-extend-below</entry>
-     <entry>Operator Strategy 10</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 20</entry>
-     <entry>operator less-than</entry>
-     <entry>Operator Strategy 5</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 21</entry>
-     <entry>operator less-than-or-equal-to</entry>
-     <entry>Operator Strategy 5</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 22</entry>
-     <entry>operator greater-than</entry>
-     <entry>Operator Strategy 1</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 23</entry>
-     <entry>operator greater-than-or-equal-to</entry>
-     <entry>Operator Strategy 1</entry>
-    </row>
-   </tbody>
-  </tgroup>
- </table>
-
- <para>
-    Support function numbers 1 through 10 are reserved for the BRIN internal
-    functions, so the SQL level functions start with number 11.  Support
-    function number 11 is the main function required to build the index.
-    It should accept two arguments with the same data type as the operator class,
-    and return the union of them.  The inclusion operator class can store union
-    values with different data types if it is defined with the
-    <literal>STORAGE</literal> parameter.  The return value of the union
-    function should match the <literal>STORAGE</literal> data type.
- </para>
-
- <para>
-    Support function numbers 12 and 14 are provided to support
-    irregularities of built-in data types.  Function number 12
-    is used to support network addresses from different families which
-    are not mergeable.  Function number 14 is used to support
-    empty ranges.  Function number 13 is an optional but
-    recommended one, which allows the new value to be checked before
-    it is passed to the union function.  As the BRIN framework can shortcut
-    some operations when the union is not changed, using this
-    function can improve index performance.
- </para>
-
- <para>
-  To write an operator class for a data type that implements only an equality
-  operator and supports hashing, it is possible to use the bloom support procedures
-  alongside the corresponding operators, as shown in
-  <xref linkend="brin-extensibility-bloom-table"/>.
-  All operator class members (procedures and operators) are mandatory.
- </para>
-
- <table id="brin-extensibility-bloom-table">
-  <title>Procedure and Support Numbers for Bloom Operator Classes</title>
-  <tgroup cols="2">
-   <thead>
-    <row>
-     <entry>Operator class member</entry>
-     <entry>Object</entry>
-    </row>
-   </thead>
-   <tbody>
-    <row>
-     <entry>Support Procedure 1</entry>
-     <entry>internal function <function>brin_bloom_opcinfo()</function></entry>
-    </row>
-    <row>
-     <entry>Support Procedure 2</entry>
-     <entry>internal function <function>brin_bloom_add_value()</function></entry>
-    </row>
-    <row>
-     <entry>Support Procedure 3</entry>
-     <entry>internal function <function>brin_bloom_consistent()</function></entry>
-    </row>
-    <row>
-     <entry>Support Procedure 4</entry>
-     <entry>internal function <function>brin_bloom_union()</function></entry>
-    </row>
-    <row>
-     <entry>Support Procedure 5</entry>
-     <entry>internal function <function>brin_bloom_options()</function></entry>
-    </row>
-    <row>
-     <entry>Support Procedure 11</entry>
-     <entry>function to compute hash of an element</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 1</entry>
-     <entry>operator equal-to</entry>
-    </row>
-   </tbody>
-  </tgroup>
- </table>
-
- <para>
-    Support procedure numbers 1-10 are reserved for the BRIN internal
-    functions, so the SQL level functions start with number 11.  Support
-    function number 11 is the main function required to build the index.
-    It should accept one argument with the same data type as the operator class,
-    and return a hash of the value.
- </para>
-
- <para>
-  The minmax-multi operator class is also intended for data types implementing
-  a totally ordered set, and may be seen as a simple extension of the minmax
-  operator class. While minmax operator class summarizes values from each block
-  range into a single contiguous interval, minmax-multi allows summarization
-  into multiple smaller intervals to improve handling of outlier values.
-  It is possible to use the minmax-multi support procedures alongside the
-  corresponding operators, as shown in
-  <xref linkend="brin-extensibility-minmax-multi-table"/>.
-  All operator class members (procedures and operators) are mandatory.
- </para>
-
- <table id="brin-extensibility-minmax-multi-table">
-  <title>Procedure and Support Numbers for minmax-multi Operator Classes</title>
-  <tgroup cols="2">
-   <thead>
-    <row>
-     <entry>Operator class member</entry>
-     <entry>Object</entry>
-    </row>
-   </thead>
-   <tbody>
-    <row>
-     <entry>Support Procedure 1</entry>
-     <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry>
-    </row>
-    <row>
-     <entry>Support Procedure 2</entry>
-     <entry>internal function <function>brin_minmax_multi_add_value()</function></entry>
-    </row>
-    <row>
-     <entry>Support Procedure 3</entry>
-     <entry>internal function <function>brin_minmax_multi_consistent()</function></entry>
-    </row>
-    <row>
-     <entry>Support Procedure 4</entry>
-     <entry>internal function <function>brin_minmax_multi_union()</function></entry>
-    </row>
-    <row>
-     <entry>Support Procedure 5</entry>
-     <entry>internal function <function>brin_minmax_multi_options()</function></entry>
-    </row>
-    <row>
-     <entry>Support Procedure 11</entry>
-     <entry>function to compute distance between two values (length of a range)</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 1</entry>
-     <entry>operator less-than</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 2</entry>
-     <entry>operator less-than-or-equal-to</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 3</entry>
-     <entry>operator equal-to</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 4</entry>
-     <entry>operator greater-than-or-equal-to</entry>
-    </row>
-    <row>
-     <entry>Operator Strategy 5</entry>
-     <entry>operator greater-than</entry>
-    </row>
-   </tbody>
-  </tgroup>
- </table>
-
- <para>
-    Both minmax and inclusion operator classes support cross-data-type
-    operators, though with these the dependencies become more complicated.
-    The minmax operator class requires a full set of operators to be
-    defined with both arguments having the same data type.  It allows
-    additional data types to be supported by defining extra sets
-    of operators.  Inclusion operator class operator strategies are dependent
-    on another operator strategy as shown in
-    <xref linkend="brin-extensibility-inclusion-table"/>, or the same
-    operator strategy as themselves.  They require the dependency
-    operator to be defined with the <literal>STORAGE</literal> data type as the
-    left-hand-side argument and the other supported data type to be the
-    right-hand-side argument of the supported operator.  See
-    <literal>float4_minmax_ops</literal> as an example of minmax, and
-    <literal>box_inclusion_ops</literal> as an example of inclusion.
- </para>
-</sect1>
-</chapter>
diff --git a/doc/src/sgml/btree.sgml b/doc/src/sgml/btree.sgml
deleted file mode 100644
index be8210286b..0000000000
--- a/doc/src/sgml/btree.sgml
+++ /dev/null
@@ -1,914 +0,0 @@
-<!-- doc/src/sgml/btree.sgml -->
-
-<chapter id="btree">
-<title>B-Tree Indexes</title>
-
-   <indexterm>
-    <primary>index</primary>
-    <secondary>B-Tree</secondary>
-   </indexterm>
-
-<sect1 id="btree-intro">
- <title>Introduction</title>
-
- <para>
-  <productname>PostgreSQL</productname> includes an implementation of the
-  standard <acronym>btree</acronym> (multi-way balanced tree) index data
-  structure.  Any data type that can be sorted into a well-defined linear
-  order can be indexed by a btree index.  The only limitation is that an
-  index entry cannot exceed approximately one-third of a page (after TOAST
-  compression, if applicable).
- </para>
-
- <para>
-  Because each btree operator class imposes a sort order on its data type,
-  btree operator classes (or, really, operator families) have come to be
-  used as <productname>PostgreSQL</productname>'s general representation
-  and understanding of sorting semantics.  Therefore, they've acquired
-  some features that go beyond what would be needed just to support btree
-  indexes, and parts of the system that are quite distant from the
-  btree <acronym>AM</acronym> make use of them.
- </para>
-
-</sect1>
-
-<sect1 id="btree-behavior">
- <title>Behavior of B-Tree Operator Classes</title>
-
- <para>
-  As shown in <xref linkend="xindex-btree-strat-table"/>, a btree operator
-  class must provide five comparison operators,
-  <literal>&lt;</literal>,
-  <literal>&lt;=</literal>,
-  <literal>=</literal>,
-  <literal>&gt;=</literal> and
-  <literal>&gt;</literal>.
-  One might expect that <literal>&lt;&gt;</literal> should also be part of
-  the operator class, but it is not, because it would almost never be
-  useful to use a <literal>&lt;&gt;</literal> WHERE clause in an index
-  search.  (For some purposes, the planner treats <literal>&lt;&gt;</literal>
-  as associated with a btree operator class; but it finds that operator via
-  the <literal>=</literal> operator's negator link, rather than
-  from <structname>pg_amop</structname>.)
- </para>
-
- <para>
-  When several data types share near-identical sorting semantics, their
-  operator classes can be grouped into an operator family.  Doing so is
-  advantageous because it allows the planner to make deductions about
-  cross-type comparisons.  Each operator class within the family should
-  contain the single-type operators (and associated support functions)
-  for its input data type, while cross-type comparison operators and
-  support functions are <quote>loose</quote> in the family.  It is
-  recommendable that a complete set of cross-type operators be included
-  in the family, thus ensuring that the planner can represent any
-  comparison conditions that it deduces from transitivity.
- </para>
-
- <para>
-  There are some basic assumptions that a btree operator family must
-  satisfy:
- </para>
-
- <itemizedlist>
-  <listitem>
-   <para>
-    An <literal>=</literal> operator must be an equivalence relation; that
-    is, for all non-null values <replaceable>A</replaceable>,
-    <replaceable>B</replaceable>, <replaceable>C</replaceable> of the
-    data type:
-
-    <itemizedlist>
-     <listitem>
-      <para>
-       <replaceable>A</replaceable> <literal>=</literal>
-       <replaceable>A</replaceable> is true
-       (<firstterm>reflexive law</firstterm>)
-      </para>
-     </listitem>
-     <listitem>
-      <para>
-       if <replaceable>A</replaceable> <literal>=</literal>
-       <replaceable>B</replaceable>,
-       then <replaceable>B</replaceable> <literal>=</literal>
-       <replaceable>A</replaceable>
-       (<firstterm>symmetric law</firstterm>)
-      </para>
-     </listitem>
-     <listitem>
-      <para>
-       if <replaceable>A</replaceable> <literal>=</literal>
-       <replaceable>B</replaceable> and <replaceable>B</replaceable>
-       <literal>=</literal> <replaceable>C</replaceable>,
-       then <replaceable>A</replaceable> <literal>=</literal>
-       <replaceable>C</replaceable>
-       (<firstterm>transitive law</firstterm>)
-      </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-  </listitem>
-
-  <listitem>
-   <para>
-    A <literal>&lt;</literal> operator must be a strong ordering relation;
-    that is, for all non-null values <replaceable>A</replaceable>,
-    <replaceable>B</replaceable>, <replaceable>C</replaceable>:
-
-    <itemizedlist>
-     <listitem>
-      <para>
-       <replaceable>A</replaceable> <literal>&lt;</literal>
-       <replaceable>A</replaceable> is false
-       (<firstterm>irreflexive law</firstterm>)
-      </para>
-     </listitem>
-     <listitem>
-      <para>
-       if <replaceable>A</replaceable> <literal>&lt;</literal>
-       <replaceable>B</replaceable>
-       and <replaceable>B</replaceable> <literal>&lt;</literal>
-       <replaceable>C</replaceable>,
-       then <replaceable>A</replaceable> <literal>&lt;</literal>
-       <replaceable>C</replaceable>
-       (<firstterm>transitive law</firstterm>)
-      </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-  </listitem>
-
-  <listitem>
-   <para>
-    Furthermore, the ordering is total; that is, for all non-null
-    values <replaceable>A</replaceable>, <replaceable>B</replaceable>:
-
-    <itemizedlist>
-     <listitem>
-      <para>
-       exactly one of <replaceable>A</replaceable> <literal>&lt;</literal>
-       <replaceable>B</replaceable>, <replaceable>A</replaceable>
-       <literal>=</literal> <replaceable>B</replaceable>, and
-       <replaceable>B</replaceable> <literal>&lt;</literal>
-       <replaceable>A</replaceable> is true
-       (<firstterm>trichotomy law</firstterm>)
-      </para>
-     </listitem>
-    </itemizedlist>
-
-    (The trichotomy law justifies the definition of the comparison support
-    function, of course.)
-   </para>
-  </listitem>
- </itemizedlist>
-
- <para>
-  The other three operators are defined in terms of <literal>=</literal>
-  and <literal>&lt;</literal> in the obvious way, and must act consistently
-  with them.
- </para>
-
- <para>
-  For an operator family supporting multiple data types, the above laws must
-  hold when <replaceable>A</replaceable>, <replaceable>B</replaceable>,
-  <replaceable>C</replaceable> are taken from any data types in the family.
-  The transitive laws are the trickiest to ensure, as in cross-type
-  situations they represent statements that the behaviors of two or three
-  different operators are consistent.
-  As an example, it would not work to put <type>float8</type>
-  and <type>numeric</type> into the same operator family, at least not with
-  the current semantics that <type>numeric</type> values are converted
-  to <type>float8</type> for comparison to a <type>float8</type>.  Because
-  of the limited accuracy of <type>float8</type>, this means there are
-  distinct <type>numeric</type> values that will compare equal to the
-  same <type>float8</type> value, and thus the transitive law would fail.
- </para>
-
- <para>
-  Another requirement for a multiple-data-type family is that any implicit
-  or binary-coercion casts that are defined between data types included in
-  the operator family must not change the associated sort ordering.
- </para>
-
- <para>
-  It should be fairly clear why a btree index requires these laws to hold
-  within a single data type: without them there is no ordering to arrange
-  the keys with.  Also, index searches using a comparison key of a
-  different data type require comparisons to behave sanely across two
-  data types.  The extensions to three or more data types within a family
-  are not strictly required by the btree index mechanism itself, but the
-  planner relies on them for optimization purposes.
- </para>
-
-</sect1>
-
-<sect1 id="btree-support-funcs">
- <title>B-Tree Support Functions</title>
-
- <para>
-  As shown in <xref linkend="xindex-btree-support-table"/>, btree defines
-  one required and four optional support functions.  The five
-  user-defined methods are:
- </para>
- <variablelist>
-  <varlistentry>
-   <term><function>order</function></term>
-   <listitem>
-    <para>
-     For each combination of data types that a btree operator family
-     provides comparison operators for, it must provide a comparison
-     support function, registered in
-     <structname>pg_amproc</structname> with support function number 1
-     and
-     <structfield>amproclefttype</structfield>/<structfield>amprocrighttype</structfield>
-     equal to the left and right data types for the comparison (i.e.,
-     the same data types that the matching operators are registered
-     with in <structname>pg_amop</structname>).  The comparison
-     function must take two non-null values
-     <replaceable>A</replaceable> and <replaceable>B</replaceable> and
-     return an <type>int32</type> value that is
-     <literal>&lt;</literal> <literal>0</literal>,
-     <literal>0</literal>, or <literal>&gt;</literal>
-     <literal>0</literal> when <replaceable>A</replaceable>
-     <literal>&lt;</literal> <replaceable>B</replaceable>,
-     <replaceable>A</replaceable> <literal>=</literal>
-     <replaceable>B</replaceable>, or <replaceable>A</replaceable>
-     <literal>&gt;</literal> <replaceable>B</replaceable>,
-     respectively.  A null result is disallowed: all values of the
-     data type must be comparable.  See
-     <filename>src/backend/access/nbtree/nbtcompare.c</filename> for
-     examples.
-    </para>
-
-    <para>
-     If the compared values are of a collatable data type, the
-     appropriate collation OID will be passed to the comparison
-     support function, using the standard
-     <function>PG_GET_COLLATION()</function> mechanism.
-    </para>
-   </listitem>
-  </varlistentry>
-  <varlistentry>
-   <term><function>sortsupport</function></term>
-   <listitem>
-    <para>
-     Optionally, a btree operator family may provide <firstterm>sort
-      support</firstterm> function(s), registered under support
-     function number 2.  These functions allow implementing
-     comparisons for sorting purposes in a more efficient way than
-     naively calling the comparison support function.  The APIs
-     involved in this are defined in
-     <filename>src/include/utils/sortsupport.h</filename>.
-    </para>
-   </listitem>
-  </varlistentry>
-  <varlistentry>
-   <term><function>in_range</function></term>
-   <listitem>
-    <indexterm>
-     <primary>in_range support functions</primary>
-    </indexterm>
-
-    <indexterm>
-     <primary>support functions</primary>
-     <secondary>in_range</secondary>
-    </indexterm>
-    <para>
-     Optionally, a btree operator family may provide
-     <firstterm>in_range</firstterm> support function(s), registered
-     under support function number 3.  These are not used during btree
-     index operations; rather, they extend the semantics of the
-     operator family so that it can support window clauses containing
-     the <literal>RANGE</literal> <replaceable>offset</replaceable>
-     <literal>PRECEDING</literal> and <literal>RANGE</literal>
-     <replaceable>offset</replaceable> <literal>FOLLOWING</literal>
-     frame bound types (see <xref
-      linkend="syntax-window-functions"/>).  Fundamentally, the extra
-     information provided is how to add or subtract an
-     <replaceable>offset</replaceable> value in a way that is
-     compatible with the family's data ordering.
-    </para>
-
-    <para>
-     An <function>in_range</function> function must have the signature
-<synopsis>
-in_range(<replaceable>val</replaceable> type1, <replaceable>base</replaceable> type1, <replaceable>offset</replaceable> type2, <replaceable>sub</replaceable> bool, <replaceable>less</replaceable> bool)
-returns bool
-</synopsis>
-     <replaceable>val</replaceable> and
-     <replaceable>base</replaceable> must be of the same type, which
-     is one of the types supported by the operator family (i.e., a
-     type for which it provides an ordering).  However,
-     <replaceable>offset</replaceable> could be of a different type,
-     which might be one otherwise unsupported by the family.  An
-     example is that the built-in <literal>time_ops</literal> family
-     provides an <function>in_range</function> function that has
-     <replaceable>offset</replaceable> of type <type>interval</type>.
-     A family can provide <function>in_range</function> functions for
-     any of its supported types and one or more
-     <replaceable>offset</replaceable> types.  Each
-     <function>in_range</function> function should be entered in
-     <structname>pg_amproc</structname> with
-     <structfield>amproclefttype</structfield> equal to
-     <type>type1</type> and <structfield>amprocrighttype</structfield>
-     equal to <type>type2</type>.
-    </para>
-
-    <para>
-     The essential semantics of an <function>in_range</function>
-     function depend on the two Boolean flag parameters.  It should
-     add or subtract <replaceable>base</replaceable> and
-     <replaceable>offset</replaceable>, then compare
-     <replaceable>val</replaceable> to the result, as follows:
-     <itemizedlist>
-      <listitem>
-       <para>
-        if <literal>!</literal><replaceable>sub</replaceable> and
-        <literal>!</literal><replaceable>less</replaceable>, return
-        <replaceable>val</replaceable> <literal>&gt;=</literal>
-        (<replaceable>base</replaceable> <literal>+</literal>
-        <replaceable>offset</replaceable>)
-       </para>
-      </listitem>
-      <listitem>
-       <para>
-        if <literal>!</literal><replaceable>sub</replaceable> and
-        <replaceable>less</replaceable>, return
-        <replaceable>val</replaceable> <literal>&lt;=</literal>
-        (<replaceable>base</replaceable> <literal>+</literal>
-        <replaceable>offset</replaceable>)
-       </para>
-      </listitem>
-      <listitem>
-       <para>
-        if <replaceable>sub</replaceable> and
-        <literal>!</literal><replaceable>less</replaceable>, return
-        <replaceable>val</replaceable> <literal>&gt;=</literal>
-        (<replaceable>base</replaceable> <literal>-</literal>
-        <replaceable>offset</replaceable>)
-       </para>
-      </listitem>
-      <listitem>
-       <para>
-        if <replaceable>sub</replaceable> and
-        <replaceable>less</replaceable>, return
-        <replaceable>val</replaceable> <literal>&lt;=</literal>
-        (<replaceable>base</replaceable> <literal>-</literal>
-        <replaceable>offset</replaceable>)
-       </para>
-      </listitem>
-     </itemizedlist>
-     Before doing so, the function should check the sign of
-     <replaceable>offset</replaceable>: if it is less than zero, raise
-     error
-     <literal>ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE</literal>
-     (22013) with error text like <quote>invalid preceding or
-      following size in window function</quote>.  (This is required by
-     the SQL standard, although nonstandard operator families might
-     perhaps choose to ignore this restriction, since there seems to
-     be little semantic necessity for it.) This requirement is
-     delegated to the <function>in_range</function> function so that
-     the core code needn't understand what <quote>less than
-      zero</quote> means for a particular data type.
-    </para>
-
-    <para>
-     An additional expectation is that <function>in_range</function>
-     functions should, if practical, avoid throwing an error if
-     <replaceable>base</replaceable> <literal>+</literal>
-     <replaceable>offset</replaceable> or
-     <replaceable>base</replaceable> <literal>-</literal>
-     <replaceable>offset</replaceable> would overflow.  The correct
-     comparison result can be determined even if that value would be
-     out of the data type's range.  Note that if the data type
-     includes concepts such as <quote>infinity</quote> or
-     <quote>NaN</quote>, extra care may be needed to ensure that
-     <function>in_range</function>'s results agree with the normal
-     sort order of the operator family.
-    </para>
-
-    <para>
-     The results of the <function>in_range</function> function must be
-     consistent with the sort ordering imposed by the operator family.
-     To be precise, given any fixed values of
-     <replaceable>offset</replaceable> and
-     <replaceable>sub</replaceable>, then:
-     <itemizedlist>
-      <listitem>
-       <para>
-        If <function>in_range</function> with
-        <replaceable>less</replaceable> = true is true for some
-        <replaceable>val1</replaceable> and
-        <replaceable>base</replaceable>, it must be true for every
-        <replaceable>val2</replaceable> <literal>&lt;=</literal>
-        <replaceable>val1</replaceable> with the same
-        <replaceable>base</replaceable>.
-       </para>
-      </listitem>
-      <listitem>
-       <para>
-        If <function>in_range</function> with
-        <replaceable>less</replaceable> = true is false for some
-        <replaceable>val1</replaceable> and
-        <replaceable>base</replaceable>, it must be false for every
-        <replaceable>val2</replaceable> <literal>&gt;=</literal>
-        <replaceable>val1</replaceable> with the same
-        <replaceable>base</replaceable>.
-       </para>
-      </listitem>
-      <listitem>
-       <para>
-        If <function>in_range</function> with
-        <replaceable>less</replaceable> = true is true for some
-        <replaceable>val</replaceable> and
-        <replaceable>base1</replaceable>, it must be true for every
-        <replaceable>base2</replaceable> <literal>&gt;=</literal>
-        <replaceable>base1</replaceable> with the same
-        <replaceable>val</replaceable>.
-       </para>
-      </listitem>
-      <listitem>
-       <para>
-        If <function>in_range</function> with
-        <replaceable>less</replaceable> = true is false for some
-        <replaceable>val</replaceable> and
-        <replaceable>base1</replaceable>, it must be false for every
-        <replaceable>base2</replaceable> <literal>&lt;=</literal>
-        <replaceable>base1</replaceable> with the same
-        <replaceable>val</replaceable>.
-       </para>
-      </listitem>
-     </itemizedlist>
-     Analogous statements with inverted conditions hold when
-     <replaceable>less</replaceable> = false.
-    </para>
-
-    <para>
-     If the type being ordered (<type>type1</type>) is collatable, the
-     appropriate collation OID will be passed to the
-     <function>in_range</function> function, using the standard
-     PG_GET_COLLATION() mechanism.
-    </para>
-
-    <para>
-     <function>in_range</function> functions need not handle NULL
-     inputs, and typically will be marked strict.
-    </para>
-   </listitem>
-  </varlistentry>
-  <varlistentry>
-   <term><function>equalimage</function></term>
-   <listitem>
-    <para>
-     Optionally, a btree operator family may provide
-     <function>equalimage</function> (<quote>equality implies image
-      equality</quote>) support functions, registered under support
-     function number 4.  These functions allow the core code to
-     determine when it is safe to apply the btree deduplication
-     optimization.  Currently, <function>equalimage</function>
-     functions are only called when building or rebuilding an index.
-    </para>
-    <para>
-     An <function>equalimage</function> function must have the
-     signature
-<synopsis>
-equalimage(<replaceable>opcintype</replaceable> <type>oid</type>) returns bool
-</synopsis>
-     The return value is static information about an operator class
-     and collation.  Returning <literal>true</literal> indicates that
-     the <function>order</function> function for the operator class is
-     guaranteed to only return <literal>0</literal> (<quote>arguments
-      are equal</quote>) when its <replaceable>A</replaceable> and
-     <replaceable>B</replaceable> arguments are also interchangeable
-     without any loss of semantic information.  Not registering an
-     <function>equalimage</function> function or returning
-     <literal>false</literal> indicates that this condition cannot be
-     assumed to hold.
-    </para>
-    <para>
-     The <replaceable>opcintype</replaceable> argument is the
-     <literal><structname>pg_type</structname>.oid</literal> of the
-     data type that the operator class indexes.  This is a convenience
-     that allows reuse of the same underlying
-     <function>equalimage</function> function across operator classes.
-     If <replaceable>opcintype</replaceable> is a collatable data
-     type, the appropriate collation OID will be passed to the
-     <function>equalimage</function> function, using the standard
-     <function>PG_GET_COLLATION()</function> mechanism.
-    </para>
-    <para>
-     As far as the operator class is concerned, returning
-     <literal>true</literal> indicates that deduplication is safe (or
-     safe for the collation whose OID was passed to its
-     <function>equalimage</function> function).  However, the core
-     code will only deem deduplication safe for an index when
-     <emphasis>every</emphasis> indexed column uses an operator class
-     that registers an <function>equalimage</function> function, and
-     each function actually returns <literal>true</literal> when
-     called.
-    </para>
-    <para>
-     Image equality is <emphasis>almost</emphasis> the same condition
-     as simple bitwise equality.  There is one subtle difference: When
-     indexing a varlena data type, the on-disk representation of two
-     image equal datums may not be bitwise equal due to inconsistent
-     application of <acronym>TOAST</acronym> compression on input.
-     Formally, when an operator class's
-     <function>equalimage</function> function returns
-     <literal>true</literal>, it is safe to assume that the
-     <literal>datum_image_eq()</literal> C function will always agree
-     with the operator class's <function>order</function> function
-     (provided that the same collation OID is passed to both the
-     <function>equalimage</function> and <function>order</function>
-     functions).
-    </para>
-    <para>
-     The core code is fundamentally unable to deduce anything about
-     the <quote>equality implies image equality</quote> status of an
-     operator class within a multiple-data-type family based on
-     details from other operator classes in the same family.  Also, it
-     is not sensible for an operator family to register a cross-type
-     <function>equalimage</function> function, and attempting to do so
-     will result in an error.  This is because <quote>equality implies
-      image equality</quote> status does not just depend on
-     sorting/equality semantics, which are more or less defined at the
-     operator family level.  In general, the semantics that one
-     particular data type implements must be considered separately.
-    </para>
-    <para>
-     The convention followed by the operator classes included with the
-     core <productname>PostgreSQL</productname> distribution is to
-     register a stock, generic <function>equalimage</function>
-     function.  Most operator classes register
-     <function>btequalimage()</function>, which indicates that
-     deduplication is safe unconditionally.  Operator classes for
-     collatable data types such as <type>text</type> register
-     <function>btvarstrequalimage()</function>, which indicates that
-     deduplication is safe with deterministic collations.  Best
-     practice for third-party extensions is to register their own
-     custom function to retain control.
-    </para>
-   </listitem>
-  </varlistentry>
-  <varlistentry>
-   <term><function>options</function></term>
-   <listitem>
-    <para>
-     Optionally, a B-tree operator family may provide
-     <function>options</function> (<quote>operator class specific
-     options</quote>) support functions, registered under support
-     function number 5.  These functions define a set of user-visible
-     parameters that control operator class behavior.
-    </para>
-    <para>
-     An <function>options</function> support function must have the
-     signature
-<synopsis>
-options(<replaceable>relopts</replaceable> <type>local_relopts *</type>) returns void
-</synopsis>
-     The function is passed a pointer to a <structname>local_relopts</structname>
-     struct, which needs to be filled with a set of operator class
-     specific options.  The options can be accessed from other support
-     functions using the <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
-     <literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
-    </para>
-    <para>
-     Currently, no B-Tree operator class has an <function>options</function>
-     support function.  B-tree doesn't allow flexible representation of keys
-     like GiST, SP-GiST, GIN and BRIN do.  So, <function>options</function>
-     probably doesn't have much application in the current B-tree index
-     access method.  Nevertheless, this support function was added to B-tree
-     for uniformity, and will probably find uses during further
-     evolution of B-tree in <productname>PostgreSQL</productname>.
-    </para>
-   </listitem>
-  </varlistentry>
- </variablelist>
-
-</sect1>
-
-<sect1 id="btree-implementation">
- <title>Implementation</title>
-
- <para>
-  This section covers B-Tree index implementation details that may be
-  of use to advanced users.  See
-  <filename>src/backend/access/nbtree/README</filename> in the source
-  distribution for a much more detailed, internals-focused description
-  of the B-Tree implementation.
- </para>
- <sect2 id="btree-structure">
-  <title>B-Tree Structure</title>
-  <para>
-   <productname>PostgreSQL</productname> B-Tree indexes are
-   multi-level tree structures, where each level of the tree can be
-   used as a doubly-linked list of pages.  A single metapage is stored
-   in a fixed position at the start of the first segment file of the
-   index.  All other pages are either leaf pages or internal pages.
-   Leaf pages are the pages on the lowest level of the tree.  All
-   other levels consist of internal pages.  Each leaf page contains
-   tuples that point to table rows.  Each internal page contains
-   tuples that point to the next level down in the tree.  Typically,
-   over 99% of all pages are leaf pages.  Both internal pages and leaf
-   pages use the standard page format described in <xref
-    linkend="storage-page-layout"/>.
-  </para>
-  <para>
-   New leaf pages are added to a B-Tree index when an existing leaf
-   page cannot fit an incoming tuple.  A <firstterm>page
-    split</firstterm> operation makes room for items that originally
-   belonged on the overflowing page by moving a portion of the items
-   to a new page.  Page splits must also insert a new
-   <firstterm>downlink</firstterm> to the new page in the parent page,
-   which may cause the parent to split in turn.  Page splits
-   <quote>cascade upwards</quote> in a recursive fashion.  When the
-   root page finally cannot fit a new downlink, a <firstterm>root page
-    split</firstterm> operation takes place.  This adds a new level to
-   the tree structure by creating a new root page that is one level
-   above the original root page.
-  </para>
- </sect2>
-
- <sect2 id="btree-deletion">
-  <title>Bottom-up Index Deletion</title>
-  <para>
-   B-Tree indexes are not directly aware that under MVCC, there might
-   be multiple extant versions of the same logical table row; to an
-   index, each tuple is an independent object that needs its own index
-   entry.  <quote>Version churn</quote> tuples may sometimes
-   accumulate and adversely affect query latency and throughput.  This
-   typically occurs with <command>UPDATE</command>-heavy workloads
-   where most individual updates cannot apply the
-   <link linkend="storage-hot"><acronym>HOT</acronym> optimization.</link>
-   Changing the value of only
-   one column covered by one index during an <command>UPDATE</command>
-   <emphasis>always</emphasis> necessitates a new set of index tuples
-   &mdash; one for <emphasis>each and every</emphasis> index on the
-   table.  Note in particular that this includes indexes that were not
-   <quote>logically modified</quote> by the <command>UPDATE</command>.
-   All indexes will need a successor physical index tuple that points
-   to the latest version in the table.  Each new tuple within each
-   index will generally need to coexist with the original
-   <quote>updated</quote> tuple for a short period of time (typically
-   until shortly after the <command>UPDATE</command> transaction
-   commits).
-  </para>
-  <para>
-   B-Tree indexes incrementally delete version churn index tuples by
-   performing <firstterm>bottom-up index deletion</firstterm> passes.
-   Each deletion pass is triggered in reaction to an anticipated
-   <quote>version churn page split</quote>.  This only happens with
-   indexes that are not logically modified by
-   <command>UPDATE</command> statements, where concentrated build up
-   of obsolete versions in particular pages would occur otherwise.  A
-   page split will usually be avoided, though it's possible that
-   certain implementation-level heuristics will fail to identify and
-   delete even one garbage index tuple (in which case a page split or
-   deduplication pass resolves the issue of an incoming new tuple not
-   fitting on a leaf page).  The worst-case number of versions that
-   any index scan must traverse (for any single logical row) is an
-   important contributor to overall system responsiveness and
-   throughput.  A bottom-up index deletion pass targets suspected
-   garbage tuples in a single leaf page based on
-   <emphasis>qualitative</emphasis> distinctions involving logical
-   rows and versions.  This contrasts with the <quote>top-down</quote>
-   index cleanup performed by autovacuum workers, which is triggered
-   when certain <emphasis>quantitative</emphasis> table-level
-   thresholds are exceeded (see <xref linkend="autovacuum"/>).
-  </para>
-  <note>
-   <para>
-    Not all deletion operations that are performed within B-Tree
-    indexes are bottom-up deletion operations.  There is a distinct
-    category of index tuple deletion: <firstterm>simple index tuple
-     deletion</firstterm>.  This is a deferred maintenance operation
-    that deletes index tuples that are known to be safe to delete
-    (those whose item identifier's <literal>LP_DEAD</literal> bit is
-    already set).  Like bottom-up index deletion, simple index
-    deletion takes place at the point that a page split is anticipated
-    as a way of avoiding the split.
-   </para>
-   <para>
-    Simple deletion is opportunistic in the sense that it can only
-    take place when recent index scans set the
-    <literal>LP_DEAD</literal> bits of affected items in passing.
-    Prior to <productname>PostgreSQL</productname> 14, the only
-    category of B-Tree deletion was simple deletion.  The main
-    differences between it and bottom-up deletion are that only the
-    former is opportunistically driven by the activity of passing
-    index scans, while only the latter specifically targets version
-    churn from <command>UPDATE</command>s that do not logically modify
-    indexed columns.
-   </para>
-  </note>
-  <para>
-   Bottom-up index deletion performs the vast majority of all garbage
-   index tuple cleanup for particular indexes with certain workloads.
-   This is expected with any B-Tree index that is subject to
-   significant version churn from <command>UPDATE</command>s that
-   rarely or never logically modify the columns that the index covers.
-   The average and worst-case number of versions per logical row can
-   be kept low purely through targeted incremental deletion passes.
-   It's quite possible that the on-disk size of certain indexes will
-   never increase by even one single page/block despite
-   <emphasis>constant</emphasis> version churn from
-   <command>UPDATE</command>s.  Even then, an exhaustive <quote>clean
-    sweep</quote> by a <command>VACUUM</command> operation (typically
-   run in an autovacuum worker process) will eventually be required as
-   a part of <emphasis>collective</emphasis> cleanup of the table and
-   each of its indexes.
-  </para>
-  <para>
-   Unlike <command>VACUUM</command>, bottom-up index deletion does not
-   provide any strong guarantees about how old the oldest garbage
-   index tuple may be.  No index can be permitted to retain
-   <quote>floating garbage</quote> index tuples that became dead prior
-   to a conservative cutoff point shared by the table and all of its
-   indexes collectively.  This fundamental table-level invariant makes
-   it safe to recycle table <acronym>TID</acronym>s.  This is how it
-   is possible for distinct logical rows to reuse the same table
-   <acronym>TID</acronym> over time (though this can never happen with
-   two logical rows whose lifetimes span the same
-   <command>VACUUM</command> cycle).
-  </para>
- </sect2>
-
- <sect2 id="btree-deduplication">
-  <title>Deduplication</title>
-  <para>
-   A duplicate is a leaf page tuple (a tuple that points to a table
-   row) where <emphasis>all</emphasis> indexed key columns have values
-   that match corresponding column values from at least one other leaf
-   page tuple in the same index.  Duplicate tuples are quite common in
-   practice.  B-Tree indexes can use a special, space-efficient
-   representation for duplicates when an optional technique is
-   enabled: <firstterm>deduplication</firstterm>.
-  </para>
-  <para>
-   Deduplication works by periodically merging groups of duplicate
-   tuples together, forming a single <firstterm>posting list</firstterm> tuple for each
-   group.  The column key value(s) only appear once in this
-   representation.  This is followed by a sorted array of
-   <acronym>TID</acronym>s that point to rows in the table.  This
-   significantly reduces the storage size of indexes where each value
-   (or each distinct combination of column values) appears several
-   times on average.  The latency of queries can be reduced
-   significantly.  Overall query throughput may increase
-   significantly.  The overhead of routine index vacuuming may also be
-   reduced significantly.
-  </para>
-  <note>
-   <para>
-    B-Tree deduplication is just as effective with
-    <quote>duplicates</quote> that contain a NULL value, even though
-    NULL values are never equal to each other according to the
-    <literal>=</literal> member of any B-Tree operator class.  As far
-    as any part of the implementation that understands the on-disk
-    B-Tree structure is concerned, NULL is just another value from the
-    domain of indexed values.
-   </para>
-  </note>
-  <para>
-   The deduplication process occurs lazily, when a new item is
-   inserted that cannot fit on an existing leaf page, though only when
-   index tuple deletion could not free sufficient space for the new
-   item (typically deletion is briefly considered and then skipped
-   over).  Unlike GIN posting list tuples, B-Tree posting list tuples
-   do not need to expand every time a new duplicate is inserted; they
-   are merely an alternative physical representation of the original
-   logical contents of the leaf page.  This design prioritizes
-   consistent performance with mixed read-write workloads.  Most
-   client applications will at least see a moderate performance
-   benefit from using deduplication.  Deduplication is enabled by
-   default.
-  </para>
-  <para>
-   <command>CREATE INDEX</command> and <command>REINDEX</command>
-   apply deduplication to create posting list tuples, though the
-   strategy they use is slightly different.  Each group of duplicate
-   ordinary tuples encountered in the sorted input taken from the
-   table is merged into a posting list tuple
-   <emphasis>before</emphasis> being added to the current pending leaf
-   page.  Individual posting list tuples are packed with as many
-   <acronym>TID</acronym>s as possible.  Leaf pages are written out in
-   the usual way, without any separate deduplication pass.  This
-   strategy is well-suited to <command>CREATE INDEX</command> and
-   <command>REINDEX</command> because they are once-off batch
-   operations.
-  </para>
-  <para>
-   Write-heavy workloads that don't benefit from deduplication due to
-   having few or no duplicate values in indexes will incur a small,
-   fixed performance penalty (unless deduplication is explicitly
-   disabled).  The <literal>deduplicate_items</literal> storage
-   parameter can be used to disable deduplication within individual
-   indexes.  There is never any performance penalty with read-only
-   workloads, since reading posting list tuples is at least as
-   efficient as reading the standard tuple representation.  Disabling
-   deduplication isn't usually helpful.
-  </para>
-  <para>
-   It is sometimes possible for unique indexes (as well as unique
-   constraints) to use deduplication.  This allows leaf pages to
-   temporarily <quote>absorb</quote> extra version churn duplicates.
-   Deduplication in unique indexes augments bottom-up index deletion,
-   especially in cases where a long-running transaction holds a
-   snapshot that blocks garbage collection.  The goal is to buy time
-   for the bottom-up index deletion strategy to become effective
-   again.  Delaying page splits until a single long-running
-   transaction naturally goes away can allow a bottom-up deletion pass
-   to succeed where an earlier deletion pass failed.
-  </para>
-  <tip>
-   <para>
-    A special heuristic is applied to determine whether a
-    deduplication pass in a unique index should take place.  It can
-    often skip straight to splitting a leaf page, avoiding a
-    performance penalty from wasting cycles on unhelpful deduplication
-    passes.  If you're concerned about the overhead of deduplication,
-    consider setting <literal>deduplicate_items = off</literal>
-    selectively.  Leaving deduplication enabled in unique indexes has
-    little downside.
-   </para>
-  </tip>
-  <para>
-   Deduplication cannot be used in all cases due to
-   implementation-level restrictions.  Deduplication safety is
-   determined when <command>CREATE INDEX</command> or
-   <command>REINDEX</command> is run.
-  </para>
-  <para>
-   Note that deduplication is deemed unsafe and cannot be used in the
-   following cases involving semantically significant differences
-   among equal datums:
-  </para>
-  <para>
-   <itemizedlist>
-    <listitem>
-     <para>
-      <type>text</type>, <type>varchar</type>, and <type>char</type>
-      cannot use deduplication when a
-      <emphasis>nondeterministic</emphasis> collation is used.  Case
-      and accent differences must be preserved among equal datums.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <type>numeric</type> cannot use deduplication.  Numeric display
-      scale must be preserved among equal datums.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <type>jsonb</type> cannot use deduplication, since the
-      <type>jsonb</type> B-Tree operator class uses
-      <type>numeric</type> internally.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <type>float4</type> and <type>float8</type> cannot use
-      deduplication.  These types have distinct representations for
-      <literal>-0</literal> and <literal>0</literal>, which are
-      nevertheless considered equal.  This difference must be
-      preserved.
-     </para>
-    </listitem>
-   </itemizedlist>
-  </para>
-  <para>
-   There is one further implementation-level restriction that may be
-   lifted in a future version of
-   <productname>PostgreSQL</productname>:
-  </para>
-  <para>
-   <itemizedlist>
-    <listitem>
-     <para>
-      Container types (such as composite types, arrays, or range
-      types) cannot use deduplication.
-     </para>
-    </listitem>
-   </itemizedlist>
-  </para>
-  <para>
-   There is one further implementation-level restriction that applies
-   regardless of the operator class or collation used:
-  </para>
-  <para>
-   <itemizedlist>
-    <listitem>
-     <para>
-      <literal>INCLUDE</literal> indexes can never use deduplication.
-     </para>
-    </listitem>
-   </itemizedlist>
-  </para>
-
- </sect2>
-</sect1>
-
-</chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index f39b4efe38..de683e3400 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -84,12 +84,7 @@
 <!ENTITY catalogs   SYSTEM "catalogs.sgml">
 <!ENTITY system-views  SYSTEM "system-views.sgml">
 <!ENTITY geqo       SYSTEM "geqo.sgml">
-<!ENTITY btree      SYSTEM "btree.sgml">
-<!ENTITY gist       SYSTEM "gist.sgml">
-<!ENTITY spgist     SYSTEM "spgist.sgml">
-<!ENTITY gin        SYSTEM "gin.sgml">
-<!ENTITY brin       SYSTEM "brin.sgml">
-<!ENTITY hash       SYSTEM "hash.sgml">
+<!ENTITY indextypes SYSTEM "indextypes.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY tableam    SYSTEM "tableam.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
diff --git a/doc/src/sgml/gin.sgml b/doc/src/sgml/gin.sgml
deleted file mode 100644
index 5bd1efae92..0000000000
--- a/doc/src/sgml/gin.sgml
+++ /dev/null
@@ -1,714 +0,0 @@
-<!-- doc/src/sgml/gin.sgml -->
-
-<chapter id="gin">
-<title>GIN Indexes</title>
-
-   <indexterm>
-    <primary>index</primary>
-    <secondary>GIN</secondary>
-   </indexterm>
-
-<sect1 id="gin-intro">
- <title>Introduction</title>
-
- <para>
-  <acronym>GIN</acronym> stands for Generalized Inverted Index.
-  <acronym>GIN</acronym> is designed for handling cases where the items
-  to be indexed are composite values, and the queries to be handled by
-  the index need to search for element values that appear within
-  the composite items.  For example, the items could be documents,
-  and the queries could be searches for documents containing specific words.
- </para>
-
- <para>
-  We use the word <firstterm>item</firstterm> to refer to a composite value that
-  is to be indexed, and the word <firstterm>key</firstterm> to refer to an element
-  value.  <acronym>GIN</acronym> always stores and searches for keys,
-  not item values per se.
- </para>
-
- <para>
-  A <acronym>GIN</acronym> index stores a set of (key, posting list) pairs,
-  where a <firstterm>posting list</firstterm> is a set of row IDs in which the key
-  occurs.  The same row ID can appear in multiple posting lists, since
-  an item can contain more than one key.  Each key value is stored only
-  once, so a <acronym>GIN</acronym> index is very compact for cases
-  where the same key appears many times.
- </para>
-
- <para>
-  <acronym>GIN</acronym> is generalized in the sense that the
-  <acronym>GIN</acronym> access method code does not need to know the
-  specific operations that it accelerates.
-  Instead, it uses custom strategies defined for particular data types.
-  The strategy defines how keys are extracted from indexed items and
-  query conditions, and how to determine whether a row that contains
-  some of the key values in a query actually satisfies the query.
- </para>
-
- <para>
-  One advantage of <acronym>GIN</acronym> is that it allows the development
-  of custom data types with the appropriate access methods, by
-  an expert in the domain of the data type, rather than a database expert.
-  This is much the same advantage as using <acronym>GiST</acronym>.
- </para>
-
- <para>
-  The <acronym>GIN</acronym>
-  implementation in <productname>PostgreSQL</productname> is primarily
-  maintained by Teodor Sigaev and Oleg Bartunov. There is more
-  information about <acronym>GIN</acronym> on their
-  <ulink url="http://www.sai.msu.su/~megera/wiki/Gin">website</ulink>.
- </para>
-</sect1>
-
-<sect1 id="gin-builtin-opclasses">
- <title>Built-in Operator Classes</title>
-
- <para>
-  The core <productname>PostgreSQL</productname> distribution
-  includes the <acronym>GIN</acronym> operator classes shown in
-  <xref linkend="gin-builtin-opclasses-table"/>.
-  (Some of the optional modules described in <xref linkend="contrib"/>
-  provide additional <acronym>GIN</acronym> operator classes.)
- </para>
-
-  <table id="gin-builtin-opclasses-table">
-   <title>Built-in <acronym>GIN</acronym> Operator Classes</title>
-   <tgroup cols="2">
-    <thead>
-     <row>
-      <entry>Name</entry>
-      <entry>Indexable Operators</entry>
-     </row>
-    </thead>
-    <tbody>
-     <row>
-      <entry morerows="3" valign="middle"><literal>array_ops</literal></entry>
-      <entry><literal>&amp;&amp; (anyarray,anyarray)</literal></entry>
-     </row>
-     <row>
-      <entry><literal>@&gt; (anyarray,anyarray)</literal></entry>
-     </row>
-     <row>
-      <entry><literal>&lt;@ (anyarray,anyarray)</literal></entry>
-     </row>
-     <row>
-      <entry><literal>= (anyarray,anyarray)</literal></entry>
-     </row>
-     <row>
-      <entry morerows="5" valign="middle"><literal>jsonb_ops</literal></entry>
-      <entry><literal>@&gt; (jsonb,jsonb)</literal></entry>
-     </row>
-     <row>
-      <entry><literal>@? (jsonb,jsonpath)</literal></entry>
-     </row>
-     <row>
-      <entry><literal>@@ (jsonb,jsonpath)</literal></entry>
-     </row>
-     <row>
-      <entry><literal>? (jsonb,text)</literal></entry>
-     </row>
-     <row>
-      <entry><literal>?| (jsonb,text[])</literal></entry>
-     </row>
-     <row>
-      <entry><literal>?&amp; (jsonb,text[])</literal></entry>
-     </row>
-     <row>
-      <entry morerows="2" valign="middle"><literal>jsonb_path_ops</literal></entry>
-      <entry><literal>@&gt; (jsonb,jsonb)</literal></entry>
-     </row>
-     <row>
-      <entry><literal>@? (jsonb,jsonpath)</literal></entry>
-     </row>
-     <row>
-      <entry><literal>@@ (jsonb,jsonpath)</literal></entry>
-     </row>
-     <row>
-      <entry valign="middle"><literal>tsvector_ops</literal></entry>
-      <entry><literal>@@ (tsvector,tsquery)</literal></entry>
-     </row>
-    </tbody>
-   </tgroup>
-  </table>
-
- <para>
-  Of the two operator classes for type <type>jsonb</type>, <literal>jsonb_ops</literal>
-  is the default.  <literal>jsonb_path_ops</literal> supports fewer operators but
-  offers better performance for those operators.
-  See <xref linkend="json-indexing"/> for details.
- </para>
-
-</sect1>
-
-<sect1 id="gin-extensibility">
- <title>Extensibility</title>
-
- <para>
-   The <acronym>GIN</acronym> interface has a high level of abstraction,
-   requiring the access method implementer only to implement the semantics of
-   the data type being accessed.  The <acronym>GIN</acronym> layer itself
-   takes care of concurrency, logging and searching the tree structure.
- </para>
-
- <para>
-   All it takes to get a <acronym>GIN</acronym> access method working is to
-   implement a few user-defined methods, which define the behavior of
-   keys in the tree and the relationships between keys, indexed items,
-   and indexable queries. In short, <acronym>GIN</acronym> combines
-   extensibility with generality, code reuse, and a clean interface.
- </para>
-
- <para>
-   There are two methods that an operator class for
-   <acronym>GIN</acronym> must provide:
-
-  <variablelist>
-    <varlistentry>
-     <term><function>Datum *extractValue(Datum itemValue, int32 *nkeys,
-        bool **nullFlags)</function></term>
-     <listitem>
-      <para>
-       Returns a palloc'd array of keys given an item to be indexed.  The
-       number of returned keys must be stored into <literal>*nkeys</literal>.
-       If any of the keys can be null, also palloc an array of
-       <literal>*nkeys</literal> <type>bool</type> fields, store its address at
-       <literal>*nullFlags</literal>, and set these null flags as needed.
-       <literal>*nullFlags</literal> can be left <symbol>NULL</symbol> (its initial value)
-       if all keys are non-null.
-       The return value can be <symbol>NULL</symbol> if the item contains no keys.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>Datum *extractQuery(Datum query, int32 *nkeys,
-        StrategyNumber n, bool **pmatch, Pointer **extra_data,
-        bool **nullFlags, int32 *searchMode)</function></term>
-     <listitem>
-      <para>
-       Returns a palloc'd array of keys given a value to be queried; that is,
-       <literal>query</literal> is the value on the right-hand side of an
-       indexable operator whose left-hand side is the indexed column.
-       <literal>n</literal> is the strategy number of the operator within the
-       operator class (see <xref linkend="xindex-strategies"/>).
-       Often, <function>extractQuery</function> will need
-       to consult <literal>n</literal> to determine the data type of
-       <literal>query</literal> and the method it should use to extract key values.
-       The number of returned keys must be stored into <literal>*nkeys</literal>.
-       If any of the keys can be null, also palloc an array of
-       <literal>*nkeys</literal> <type>bool</type> fields, store its address at
-       <literal>*nullFlags</literal>, and set these null flags as needed.
-       <literal>*nullFlags</literal> can be left <symbol>NULL</symbol> (its initial value)
-       if all keys are non-null.
-       The return value can be <symbol>NULL</symbol> if the <literal>query</literal> contains no keys.
-      </para>
-
-      <para>
-       <literal>searchMode</literal> is an output argument that allows
-       <function>extractQuery</function> to specify details about how the search
-       will be done.
-       If <literal>*searchMode</literal> is set to
-       <literal>GIN_SEARCH_MODE_DEFAULT</literal> (which is the value it is
-       initialized to before call), only items that match at least one of
-       the returned keys are considered candidate matches.
-       If <literal>*searchMode</literal> is set to
-       <literal>GIN_SEARCH_MODE_INCLUDE_EMPTY</literal>, then in addition to items
-       containing at least one matching key, items that contain no keys at
-       all are considered candidate matches.  (This mode is useful for
-       implementing is-subset-of operators, for example.)
-       If <literal>*searchMode</literal> is set to <literal>GIN_SEARCH_MODE_ALL</literal>,
-       then all non-null items in the index are considered candidate
-       matches, whether they match any of the returned keys or not.  (This
-       mode is much slower than the other two choices, since it requires
-       scanning essentially the entire index, but it may be necessary to
-       implement corner cases correctly.  An operator that needs this mode
-       in most cases is probably not a good candidate for a GIN operator
-       class.)
-       The symbols to use for setting this mode are defined in
-       <filename>access/gin.h</filename>.
-      </para>
-
-      <para>
-       <literal>pmatch</literal> is an output argument for use when partial match
-       is supported.  To use it, <function>extractQuery</function> must allocate
-       an array of <literal>*nkeys</literal> <type>bool</type>s and store its address at
-       <literal>*pmatch</literal>.  Each element of the array should be set to true
-       if the corresponding key requires partial match, false if not.
-       If <literal>*pmatch</literal> is set to <symbol>NULL</symbol> then GIN assumes partial match
-       is not required.  The variable is initialized to <symbol>NULL</symbol> before call,
-       so this argument can simply be ignored by operator classes that do
-       not support partial match.
-      </para>
-
-      <para>
-       <literal>extra_data</literal> is an output argument that allows
-       <function>extractQuery</function> to pass additional data to the
-       <function>consistent</function> and <function>comparePartial</function> methods.
-       To use it, <function>extractQuery</function> must allocate
-       an array of <literal>*nkeys</literal> pointers and store its address at
-       <literal>*extra_data</literal>, then store whatever it wants to into the
-       individual pointers.  The variable is initialized to <symbol>NULL</symbol> before
-       call, so this argument can simply be ignored by operator classes that
-       do not require extra data.  If <literal>*extra_data</literal> is set, the
-       whole array is passed to the <function>consistent</function> method, and
-       the appropriate element to the <function>comparePartial</function> method.
-      </para>
-
-     </listitem>
-    </varlistentry>
-  </variablelist>
-
-  An operator class must also provide a function to check if an indexed item
-  matches the query. It comes in two flavors, a Boolean <function>consistent</function>
-  function, and a ternary <function>triConsistent</function> function.
-  <function>triConsistent</function> covers the functionality of both, so providing
-  <function>triConsistent</function> alone is sufficient. However, if the Boolean
-  variant is significantly cheaper to calculate, it can be advantageous to
-  provide both.  If only the Boolean variant is provided, some optimizations
-  that depend on refuting index items before fetching all the keys are
-  disabled.
-
-  <variablelist>
-    <varlistentry>
-     <term><function>bool consistent(bool check[], StrategyNumber n, Datum query,
-        int32 nkeys, Pointer extra_data[], bool *recheck,
-        Datum queryKeys[], bool nullFlags[])</function></term>
-     <listitem>
-      <para>
-       Returns true if an indexed item satisfies the query operator with
-       strategy number <literal>n</literal> (or might satisfy it, if the recheck
-       indication is returned).  This function does not have direct access
-       to the indexed item's value, since <acronym>GIN</acronym> does not
-       store items explicitly.  Rather, what is available is knowledge
-       about which key values extracted from the query appear in a given
-       indexed item.  The <literal>check</literal> array has length
-       <literal>nkeys</literal>, which is the same as the number of keys previously
-       returned by <function>extractQuery</function> for this <literal>query</literal> datum.
-       Each element of the
-       <literal>check</literal> array is true if the indexed item contains the
-       corresponding query key, i.e., if (check[i] == true) the i-th key of the
-       <function>extractQuery</function> result array is present in the indexed item.
-       The original <literal>query</literal> datum is
-       passed in case the <function>consistent</function> method needs to consult it,
-       and so are the <literal>queryKeys[]</literal> and <literal>nullFlags[]</literal>
-       arrays previously returned by <function>extractQuery</function>.
-       <literal>extra_data</literal> is the extra-data array returned by
-       <function>extractQuery</function>, or <symbol>NULL</symbol> if none.
-      </para>
-
-      <para>
-       When <function>extractQuery</function> returns a null key in
-       <literal>queryKeys[]</literal>, the corresponding <literal>check[]</literal> element
-       is true if the indexed item contains a null key; that is, the
-       semantics of <literal>check[]</literal> are like <literal>IS NOT DISTINCT
-       FROM</literal>.  The <function>consistent</function> function can examine the
-       corresponding <literal>nullFlags[]</literal> element if it needs to tell
-       the difference between a regular value match and a null match.
-      </para>
-
-      <para>
-       On success, <literal>*recheck</literal> should be set to true if the heap
-       tuple needs to be rechecked against the query operator, or false if
-       the index test is exact.  That is, a false return value guarantees
-       that the heap tuple does not match the query; a true return value with
-       <literal>*recheck</literal> set to false guarantees that the heap tuple does
-       match the query; and a true return value with
-       <literal>*recheck</literal> set to true means that the heap tuple might match
-       the query, so it needs to be fetched and rechecked by evaluating the
-       query operator directly against the originally indexed item.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>GinTernaryValue triConsistent(GinTernaryValue check[], StrategyNumber n, Datum query,
-        int32 nkeys, Pointer extra_data[],
-        Datum queryKeys[], bool nullFlags[])</function></term>
-     <listitem>
-      <para>
-       <function>triConsistent</function> is similar to <function>consistent</function>,
-       but instead of Booleans in the <literal>check</literal> vector, there are
-       three possible values for each
-       key: <literal>GIN_TRUE</literal>, <literal>GIN_FALSE</literal> and
-       <literal>GIN_MAYBE</literal>. <literal>GIN_FALSE</literal> and <literal>GIN_TRUE</literal>
-       have the same meaning as regular Boolean values, while
-       <literal>GIN_MAYBE</literal> means that the presence of that key is not known.
-       When <literal>GIN_MAYBE</literal> values are present, the function should only
-       return <literal>GIN_TRUE</literal> if the item certainly matches whether or
-       not the index item contains the corresponding query keys. Likewise, the
-       function must return <literal>GIN_FALSE</literal> only if the item certainly
-       does not match, whether or not it contains the <literal>GIN_MAYBE</literal>
-       keys. If the result depends on the <literal>GIN_MAYBE</literal> entries, i.e.,
-       the match cannot be confirmed or refuted based on the known query keys,
-       the function must return <literal>GIN_MAYBE</literal>.
-      </para>
-      <para>
-       When there are no <literal>GIN_MAYBE</literal> values in the <literal>check</literal>
-       vector, a <literal>GIN_MAYBE</literal> return value is the equivalent of
-       setting the <literal>recheck</literal> flag in the
-       Boolean <function>consistent</function> function.
-      </para>
-     </listitem>
-    </varlistentry>
-  </variablelist>
- </para>
-
- <para>
-  In addition, GIN must have a way to sort the key values stored in the index.
-  The operator class can define the sort ordering by specifying a comparison
-  method:
-
-  <variablelist>
-    <varlistentry>
-     <term><function>int compare(Datum a, Datum b)</function></term>
-     <listitem>
-      <para>
-       Compares two keys (not indexed items!) and returns an integer less than
-       zero, zero, or greater than zero, indicating whether the first key is
-       less than, equal to, or greater than the second.  Null keys are never
-       passed to this function.
-      </para>
-     </listitem>
-    </varlistentry>
-  </variablelist>
-
-  Alternatively, if the operator class does not provide a <function>compare</function>
-  method, GIN will look up the default btree operator class for the index
-  key data type, and use its comparison function.  It is recommended to
-  specify the comparison function in a GIN operator class that is meant for
-  just one data type, as looking up the btree operator class costs a few
-  cycles.  However, polymorphic GIN operator classes (such
-  as <literal>array_ops</literal>) typically cannot specify a single comparison
-  function.
- </para>
-
- <para>
-  An operator class for <acronym>GIN</acronym> can optionally supply the
-  following methods:
-
-  <variablelist>
-    <varlistentry>
-     <term><function>int comparePartial(Datum partial_key, Datum key, StrategyNumber n,
-                              Pointer extra_data)</function></term>
-     <listitem>
-      <para>
-       Compare a partial-match query key to an index key.  Returns an integer
-       whose sign indicates the result: less than zero means the index key
-       does not match the query, but the index scan should continue; zero
-       means that the index key does match the query; greater than zero
-       indicates that the index scan should stop because no more matches
-       are possible.  The strategy number <literal>n</literal> of the operator
-       that generated the partial match query is provided, in case its
-       semantics are needed to determine when to end the scan.  Also,
-       <literal>extra_data</literal> is the corresponding element of the extra-data
-       array made by <function>extractQuery</function>, or <symbol>NULL</symbol> if none.
-       Null keys are never passed to this function.
-      </para>
-     </listitem>
-    </varlistentry>
-    <varlistentry>
-     <term><function>void options(local_relopts *relopts)</function></term>
-     <listitem>
-      <para>
-       Defines a set of user-visible parameters that control operator class
-       behavior.
-      </para>
-
-      <para>
-       The <function>options</function> function is passed a pointer to a
-       <structname>local_relopts</structname> struct, which needs to be
-       filled with a set of operator class specific options.  The options
-       can be accessed from other support functions using the
-       <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
-       <literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
-      </para>
-
-      <para>
-       Since both key extraction of indexed values and representation of the
-       key in <acronym>GIN</acronym> are flexible, they may depend on
-       user-specified parameters.
-      </para>
-     </listitem>
-    </varlistentry>
-  </variablelist>
- </para>
-
- <para>
-  To support <quote>partial match</quote> queries, an operator class must
-  provide the <function>comparePartial</function> method, and its
-  <function>extractQuery</function> method must set the <literal>pmatch</literal>
-  parameter when a partial-match query is encountered.  See
-  <xref linkend="gin-partial-match"/> for details.
- </para>
-
- <para>
-  The actual data types of the various <literal>Datum</literal> values mentioned
-  above vary depending on the operator class.  The item values passed to
-  <function>extractValue</function> are always of the operator class's input type, and
-  all key values must be of the class's <literal>STORAGE</literal> type.  The type of
-  the <literal>query</literal> argument passed to <function>extractQuery</function>,
-  <function>consistent</function> and <function>triConsistent</function> is whatever is the
-  right-hand input type of the class member operator identified by the
-  strategy number.  This need not be the same as the indexed type, so long as
-  key values of the correct type can be extracted from it.  However, it is
-  recommended that the SQL declarations of these three support functions use
-  the opclass's indexed data type for the <literal>query</literal> argument, even
-  though the actual type might be something else depending on the operator.
- </para>
-
-</sect1>
-
-<sect1 id="gin-implementation">
- <title>Implementation</title>
-
- <para>
-  Internally, a <acronym>GIN</acronym> index contains a B-tree index
-  constructed over keys, where each key is an element of one or more indexed
-  items (a member of an array, for example) and where each tuple in a leaf
-  page contains either a pointer to a B-tree of heap pointers (a
-  <quote>posting tree</quote>), or a simple list of heap pointers (a <quote>posting
-  list</quote>) when the list is small enough to fit into a single index tuple along
-  with the key value.  <xref linkend="gin-internals-figure"/> illustrates
-  these components of a GIN index.
- </para>
-
- <para>
-  As of <productname>PostgreSQL</productname> 9.1, null key values can be
-  included in the index.  Also, placeholder nulls are included in the index
-  for indexed items that are null or contain no keys according to
-  <function>extractValue</function>.  This allows searches that should find empty
-  items to do so.
- </para>
-
- <para>
-  Multicolumn <acronym>GIN</acronym> indexes are implemented by building
-  a single B-tree over composite values (column number, key value).  The
-  key values for different columns can be of different types.
- </para>
-
- <figure id="gin-internals-figure">
-  <title>GIN Internals</title>
-  <mediaobject>
-   <imageobject>
-    <imagedata fileref="images/gin.svg" format="SVG" width="100%"/>
-   </imageobject>
-  </mediaobject>
- </figure>
-
- <sect2 id="gin-fast-update">
-  <title>GIN Fast Update Technique</title>
-
-  <para>
-   Updating a <acronym>GIN</acronym> index tends to be slow because of the
-   intrinsic nature of inverted indexes: inserting or updating one heap row
-   can cause many inserts into the index (one for each key extracted
-   from the indexed item).
-   <acronym>GIN</acronym> is capable of postponing much of this work by inserting
-   new tuples into a temporary, unsorted list of pending entries.
-   When the table is vacuumed or autoanalyzed, or when
-   <function>gin_clean_pending_list</function> function is called, or if the
-   pending list becomes larger than
-   <xref linkend="guc-gin-pending-list-limit"/>, the entries are moved to the
-   main <acronym>GIN</acronym> data structure using the same bulk insert
-   techniques used during initial index creation.  This greatly improves
-   <acronym>GIN</acronym> index update speed, even counting the additional
-   vacuum overhead.  Moreover the overhead work can be done by a background
-   process instead of in foreground query processing.
-  </para>
-
-  <para>
-   The main disadvantage of this approach is that searches must scan the list
-   of pending entries in addition to searching the regular index, and so
-   a large list of pending entries will slow searches significantly.
-   Another disadvantage is that, while most updates are fast, an update
-   that causes the pending list to become <quote>too large</quote> will incur an
-   immediate cleanup cycle and thus be much slower than other updates.
-   Proper use of autovacuum can minimize both of these problems.
-  </para>
-
-  <para>
-   If consistent response time is more important than update speed,
-   use of pending entries can be disabled by turning off the
-   <literal>fastupdate</literal> storage parameter for a
-   <acronym>GIN</acronym> index.  See <xref linkend="sql-createindex"/>
-   for details.
-  </para>
- </sect2>
-
- <sect2 id="gin-partial-match">
-  <title>Partial Match Algorithm</title>
-
-  <para>
-   GIN can support <quote>partial match</quote> queries, in which the query
-   does not determine an exact match for one or more keys, but the possible
-   matches fall within a reasonably narrow range of key values (within the
-   key sorting order determined by the <function>compare</function> support method).
-   The <function>extractQuery</function> method, instead of returning a key value
-   to be matched exactly, returns a key value that is the lower bound of
-   the range to be searched, and sets the <literal>pmatch</literal> flag true.
-   The key range is then scanned using the <function>comparePartial</function>
-   method.  <function>comparePartial</function> must return zero for a matching
-   index key, less than zero for a non-match that is still within the range
-   to be searched, or greater than zero if the index key is past the range
-   that could match.
-  </para>
- </sect2>
-
-</sect1>
-
-<sect1 id="gin-tips">
-<title>GIN Tips and Tricks</title>
-
- <variablelist>
-  <varlistentry>
-   <term>Create vs. insert</term>
-   <listitem>
-    <para>
-     Insertion into a <acronym>GIN</acronym> index can be slow
-     due to the likelihood of many keys being inserted for each item.
-     So, for bulk insertions into a table it is advisable to drop the GIN
-     index and recreate it after finishing bulk insertion.
-    </para>
-
-    <para>
-     When <literal>fastupdate</literal> is enabled for <acronym>GIN</acronym>
-     (see <xref linkend="gin-fast-update"/> for details), the penalty is
-     less than when it is not.  But for very large updates it may still be
-     best to drop and recreate the index.
-    </para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><xref linkend="guc-maintenance-work-mem"/></term>
-   <listitem>
-    <para>
-     Build time for a <acronym>GIN</acronym> index is very sensitive to
-     the <varname>maintenance_work_mem</varname> setting; it doesn't pay to
-     skimp on work memory during index creation.
-    </para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><xref linkend="guc-gin-pending-list-limit"/></term>
-   <listitem>
-    <para>
-     During a series of insertions into an existing <acronym>GIN</acronym>
-     index that has <literal>fastupdate</literal> enabled, the system will clean up
-     the pending-entry list whenever the list grows larger than
-     <varname>gin_pending_list_limit</varname>. To avoid fluctuations in observed
-     response time, it's desirable to have pending-list cleanup occur in the
-     background (i.e., via autovacuum).  Foreground cleanup operations
-     can be avoided by increasing <varname>gin_pending_list_limit</varname>
-     or making autovacuum more aggressive.
-     However, enlarging the threshold of the cleanup operation means that
-     if a foreground cleanup does occur, it will take even longer.
-    </para>
-    <para>
-     <varname>gin_pending_list_limit</varname> can be overridden for individual
-     GIN indexes by changing storage parameters, which allows each
-     GIN index to have its own cleanup threshold.
-     For example, it's possible to increase the threshold only for the GIN
-     index which can be updated heavily, and decrease it otherwise.
-    </para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><xref linkend="guc-gin-fuzzy-search-limit"/></term>
-   <listitem>
-    <para>
-     The primary goal of developing <acronym>GIN</acronym> indexes was
-     to create support for highly scalable full-text search in
-     <productname>PostgreSQL</productname>, and there are often situations when
-     a full-text search returns a very large set of results.  Moreover, this
-     often happens when the query contains very frequent words, so that the
-     large result set is not even useful.  Since reading many
-     tuples from the disk and sorting them could take a lot of time, this is
-     unacceptable for production.  (Note that the index search itself is very
-     fast.)
-    </para>
-    <para>
-     To facilitate controlled execution of such queries,
-     <acronym>GIN</acronym> has a configurable soft upper limit on the
-     number of rows returned: the
-     <varname>gin_fuzzy_search_limit</varname> configuration parameter.
-     It is set to 0 (meaning no limit) by default.
-     If a non-zero limit is set, then the returned set is a subset of
-     the whole result set, chosen at random.
-    </para>
-    <para>
-     <quote>Soft</quote> means that the actual number of returned results
-     could differ somewhat from the specified limit, depending on the query
-     and the quality of the system's random number generator.
-    </para>
-    <para>
-     From experience, values in the thousands (e.g., 5000 &mdash; 20000)
-     work well.
-    </para>
-   </listitem>
-  </varlistentry>
- </variablelist>
-
-</sect1>
-
-<sect1 id="gin-limit">
- <title>Limitations</title>
-
- <para>
-  <acronym>GIN</acronym> assumes that indexable operators are strict.  This
-  means that <function>extractValue</function> will not be called at all on a null
-  item value (instead, a placeholder index entry is created automatically),
-  and <function>extractQuery</function> will not be called on a null query
-  value either (instead, the query is presumed to be unsatisfiable).  Note
-  however that null key values contained within a non-null composite item
-  or query value are supported.
- </para>
-</sect1>
-
-<sect1 id="gin-examples">
- <title>Examples</title>
-
- <para>
-  The core <productname>PostgreSQL</productname> distribution
-  includes the <acronym>GIN</acronym> operator classes previously shown in
-  <xref linkend="gin-builtin-opclasses-table"/>.
-  The following <filename>contrib</filename> modules also contain
-  <acronym>GIN</acronym> operator classes:
-
- <variablelist>
-  <varlistentry>
-   <term><filename>btree_gin</filename></term>
-   <listitem>
-    <para>B-tree equivalent functionality for several data types</para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><filename>hstore</filename></term>
-   <listitem>
-    <para>Module for storing (key, value) pairs</para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><filename>intarray</filename></term>
-   <listitem>
-    <para>Enhanced support for <type>int[]</type></para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><filename>pg_trgm</filename></term>
-   <listitem>
-    <para>Text similarity using trigram matching</para>
-   </listitem>
-  </varlistentry>
- </variablelist>
- </para>
-</sect1>
-
-</chapter>
diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml
deleted file mode 100644
index 8a19f156d8..0000000000
--- a/doc/src/sgml/gist.sgml
+++ /dev/null
@@ -1,1387 +0,0 @@
-<!-- doc/src/sgml/gist.sgml -->
-
-<chapter id="gist">
-<title>GiST Indexes</title>
-
-   <indexterm>
-    <primary>index</primary>
-    <secondary>GiST</secondary>
-   </indexterm>
-
-<sect1 id="gist-intro">
- <title>Introduction</title>
-
- <para>
-   <acronym>GiST</acronym> stands for Generalized Search Tree.  It is a
-   balanced, tree-structured access method, that acts as a base template in
-   which to implement arbitrary indexing schemes. B-trees, R-trees and many
-   other indexing schemes can be implemented in <acronym>GiST</acronym>.
- </para>
-
- <para>
-  One advantage of <acronym>GiST</acronym> is that it allows the development
-  of custom data types with the appropriate access methods, by
-  an expert in the domain of the data type, rather than a database expert.
- </para>
-
-  <para>
-    Some of the information here is derived from the University of California
-    at Berkeley's GiST Indexing Project
-    <ulink url="http://gist.cs.berkeley.edu/">web site</ulink> and
-    Marcel Kornacker's thesis,
-    <ulink url="http://www.sai.msu.su/~megera/postgres/gist/papers/concurrency/access-methods-for-next-generation.pdf.gz">
-    Access Methods for Next-Generation Database Systems</ulink>.
-    The <acronym>GiST</acronym>
-    implementation in <productname>PostgreSQL</productname> is primarily
-    maintained by Teodor Sigaev and Oleg Bartunov, and there is more
-    information on their
-    <ulink url="http://www.sai.msu.su/~megera/postgres/gist/">web site</ulink>.
-  </para>
-
-</sect1>
-
-<sect1 id="gist-builtin-opclasses">
- <title>Built-in Operator Classes</title>
-
- <para>
-  The core <productname>PostgreSQL</productname> distribution
-  includes the <acronym>GiST</acronym> operator classes shown in
-  <xref linkend="gist-builtin-opclasses-table"/>.
-  (Some of the optional modules described in <xref linkend="contrib"/>
-  provide additional <acronym>GiST</acronym> operator classes.)
- </para>
-
-  <table id="gist-builtin-opclasses-table">
-   <title>Built-in <acronym>GiST</acronym> Operator Classes</title>
-   <tgroup cols="3">
-     <colspec colname="col1" colwidth="2*"/>
-     <colspec colname="col2" colwidth="3*"/>
-     <colspec colname="col3" colwidth="2*"/>
-    <thead>
-     <row>
-      <entry>Name</entry>
-      <entry>Indexable Operators</entry>
-      <entry>Ordering Operators</entry>
-     </row>
-    </thead>
-    <tbody>
-     <row>
-      <entry valign="middle" morerows="13"><literal>box_ops</literal></entry>
-      <entry><literal>&lt;&lt; (box, box)</literal></entry>
-      <entry valign="middle" morerows="13"><literal>&lt;-&gt; (box, point)</literal></entry>
-     </row>
-     <row><entry><literal>&amp;&lt; (box, box)</literal></entry></row>
-     <row><entry><literal>&amp;&amp; (box, box)</literal></entry></row>
-     <row><entry><literal>&amp;&gt; (box, box)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (box, box)</literal></entry></row>
-     <row><entry><literal>~= (box, box)</literal></entry></row>
-     <row><entry><literal>@&gt; (box, box)</literal></entry></row>
-     <row><entry><literal>&lt;@ (box, box)</literal></entry></row>
-     <row><entry><literal>&amp;&lt;| (box, box)</literal></entry></row>
-     <row><entry><literal>&lt;&lt;| (box, box)</literal></entry></row>
-     <row><entry><literal>|&gt;&gt; (box, box)</literal></entry></row>
-     <row><entry><literal>|&amp;&gt; (box, box)</literal></entry></row>
-     <row><entry><literal>~ (box, box)</literal></entry></row>
-     <row><entry><literal>@ (box, box)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="13"><literal>circle_ops</literal></entry>
-      <entry><literal>&lt;&lt; (circle, circle)</literal></entry>
-      <entry valign="middle" morerows="13"><literal>&lt;-&gt; (circle, point)</literal></entry>
-     </row>
-     <row><entry><literal>&amp;&lt; (circle, circle)</literal></entry></row>
-     <row><entry><literal>&amp;&gt; (circle, circle)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (circle, circle)</literal></entry></row>
-     <row><entry><literal>&lt;@ (circle, circle)</literal></entry></row>
-     <row><entry><literal>@&gt; (circle, circle)</literal></entry></row>
-     <row><entry><literal>~= (circle, circle)</literal></entry></row>
-     <row><entry><literal>&amp;&amp; (circle, circle)</literal></entry></row>
-     <row><entry><literal>|&gt;&gt; (circle, circle)</literal></entry></row>
-     <row><entry><literal>&lt;&lt;| (circle, circle)</literal></entry></row>
-     <row><entry><literal>&amp;&lt;| (circle, circle)</literal></entry></row>
-     <row><entry><literal>|&amp;&gt; (circle, circle)</literal></entry></row>
-     <row><entry><literal>@ (circle, circle)</literal></entry></row>
-     <row><entry><literal>~ (circle, circle)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="10"><literal>inet_ops</literal></entry>
-      <entry><literal>&lt;&lt; (inet, inet)</literal></entry>
-      <entry valign="middle" morerows="10"></entry>
-     </row>
-     <row><entry><literal>&lt;&lt;= (inet, inet)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (inet, inet)</literal></entry></row>
-     <row><entry><literal>&gt;&gt;= (inet, inet)</literal></entry></row>
-     <row><entry><literal>= (inet, inet)</literal></entry></row>
-     <row><entry><literal>&lt;&gt; (inet, inet)</literal></entry></row>
-     <row><entry><literal>&lt; (inet, inet)</literal></entry></row>
-     <row><entry><literal>&lt;= (inet, inet)</literal></entry></row>
-     <row><entry><literal>&gt; (inet, inet)</literal></entry></row>
-     <row><entry><literal>&gt;= (inet, inet)</literal></entry></row>
-     <row><entry><literal>&amp;&amp; (inet, inet)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="17"><literal>multirange_ops</literal></entry>
-      <entry><literal>= (anymultirange, anymultirange)</literal></entry>
-      <entry valign="middle" morerows="17"></entry>
-     </row>
-     <row><entry><literal>&amp;&amp; (anymultirange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&amp;&amp; (anymultirange, anyrange)</literal></entry></row>
-     <row><entry><literal>@&gt; (anymultirange, anyelement)</literal></entry></row>
-     <row><entry><literal>@&gt; (anymultirange, anymultirange)</literal></entry></row>
-     <row><entry><literal>@&gt; (anymultirange, anyrange)</literal></entry></row>
-     <row><entry><literal>&lt;@ (anymultirange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&lt;@ (anymultirange, anyrange)</literal></entry></row>
-     <row><entry><literal>&lt;&lt; (anymultirange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&lt;&lt; (anymultirange, anyrange)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (anymultirange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (anymultirange, anyrange)</literal></entry></row>
-     <row><entry><literal>&amp;&lt; (anymultirange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&amp;&lt; (anymultirange, anyrange)</literal></entry></row>
-     <row><entry><literal>&amp;&gt; (anymultirange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&amp;&gt; (anymultirange, anyrange)</literal></entry></row>
-     <row><entry><literal>-|- (anymultirange, anymultirange)</literal></entry></row>
-     <row><entry><literal>-|- (anymultirange, anyrange)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="7"><literal>point_ops</literal></entry>
-      <entry><literal>|&gt;&gt; (point, point)</literal></entry>
-      <entry valign="middle" morerows="7"><literal>&lt;-&gt; (point, point)</literal></entry>
-     </row>
-     <row><entry><literal>&lt;&lt; (point, point)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (point, point)</literal></entry></row>
-     <row><entry><literal>&lt;&lt;| (point, point)</literal></entry></row>
-     <row><entry><literal>~= (point, point)</literal></entry></row>
-     <row><entry><literal>&lt;@ (point, box)</literal></entry></row>
-     <row><entry><literal>&lt;@ (point, polygon)</literal></entry></row>
-     <row><entry><literal>&lt;@ (point, circle)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="13"><literal>poly_ops</literal></entry>
-      <entry><literal>&lt;&lt; (polygon, polygon)</literal></entry>
-      <entry valign="middle" morerows="13"><literal>&lt;-&gt; (polygon, point)</literal></entry>
-     </row>
-     <row><entry><literal>&amp;&lt; (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>&amp;&gt; (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>&lt;@ (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>@&gt; (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>~= (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>&amp;&amp; (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>&lt;&lt;| (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>&amp;&lt;| (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>|&amp;&gt; (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>|&gt;&gt; (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>@ (polygon, polygon)</literal></entry></row>
-     <row><entry><literal>~ (polygon, polygon)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="17"><literal>range_ops</literal></entry>
-      <entry><literal>= (anyrange, anyrange)</literal></entry>
-      <entry valign="middle" morerows="17"></entry>
-     </row>
-     <row><entry><literal>&amp;&amp; (anyrange, anyrange)</literal></entry></row>
-     <row><entry><literal>&amp;&amp; (anyrange, anymultirange)</literal></entry></row>
-     <row><entry><literal>@&gt; (anyrange, anyelement)</literal></entry></row>
-     <row><entry><literal>@&gt; (anyrange, anyrange)</literal></entry></row>
-     <row><entry><literal>@&gt; (anyrange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&lt;@ (anyrange, anyrange)</literal></entry></row>
-     <row><entry><literal>&lt;@ (anyrange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&lt;&lt; (anyrange, anyrange)</literal></entry></row>
-     <row><entry><literal>&lt;&lt; (anyrange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (anyrange, anyrange)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (anyrange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&amp;&lt; (anyrange, anyrange)</literal></entry></row>
-     <row><entry><literal>&amp;&lt; (anyrange, anymultirange)</literal></entry></row>
-     <row><entry><literal>&amp;&gt; (anyrange, anyrange)</literal></entry></row>
-     <row><entry><literal>&amp;&gt; (anyrange, anymultirange)</literal></entry></row>
-     <row><entry><literal>-|- (anyrange, anyrange)</literal></entry></row>
-     <row><entry><literal>-|- (anyrange, anymultirange)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="1"><literal>tsquery_ops</literal></entry>
-      <entry><literal>&lt;@ (tsquery, tsquery)</literal></entry>
-      <entry valign="middle" morerows="1"></entry>
-     </row>
-     <row><entry><literal>@&gt; (tsquery, tsquery)</literal></entry></row>
-     <row>
-      <entry valign="middle"><literal>tsvector_ops</literal></entry>
-      <entry><literal>@@ (tsvector, tsquery)</literal></entry>
-      <entry></entry>
-     </row>
-    </tbody>
-   </tgroup>
-  </table>
-
- <para>
-  For historical reasons, the <literal>inet_ops</literal> operator class is
-  not the default class for types <type>inet</type> and <type>cidr</type>.
-  To use it, mention the class name in <command>CREATE INDEX</command>,
-  for example
-<programlisting>
-CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops);
-</programlisting>
- </para>
-
-</sect1>
-
-<sect1 id="gist-extensibility">
- <title>Extensibility</title>
-
- <para>
-   Traditionally, implementing a new index access method meant a lot of
-   difficult work.  It was necessary to understand the inner workings of the
-   database, such as the lock manager and Write-Ahead Log.  The
-   <acronym>GiST</acronym> interface has a high level of abstraction,
-   requiring the access method implementer only to implement the semantics of
-   the data type being accessed.  The <acronym>GiST</acronym> layer itself
-   takes care of concurrency, logging and searching the tree structure.
- </para>
-
- <para>
-   This extensibility should not be confused with the extensibility of the
-   other standard search trees in terms of the data they can handle.  For
-   example, <productname>PostgreSQL</productname> supports extensible B-trees
-   and hash indexes. That means that you can use
-   <productname>PostgreSQL</productname> to build a B-tree or hash over any
-   data type you want. But B-trees only support range predicates
-   (<literal>&lt;</literal>, <literal>=</literal>, <literal>&gt;</literal>),
-   and hash indexes only support equality queries.
- </para>
-
- <para>
-   So if you index, say, an image collection with a
-   <productname>PostgreSQL</productname> B-tree, you can only issue queries
-   such as <quote>is imagex equal to imagey</quote>, <quote>is imagex less
-   than imagey</quote> and <quote>is imagex greater than imagey</quote>.
-   Depending on how you define <quote>equals</quote>, <quote>less than</quote>
-   and <quote>greater than</quote> in this context, this could be useful.
-   However, by using a <acronym>GiST</acronym> based index, you could create
-   ways to ask domain-specific questions, perhaps <quote>find all images of
-   horses</quote> or <quote>find all over-exposed images</quote>.
- </para>
-
- <para>
-   All it takes to get a <acronym>GiST</acronym> access method up and running
-   is to implement several user-defined methods, which define the behavior of
-   keys in the tree. Of course these methods have to be pretty fancy to
-   support fancy queries, but for all the standard queries (B-trees,
-   R-trees, etc.) they're relatively straightforward. In short,
-   <acronym>GiST</acronym> combines extensibility along with generality, code
-   reuse, and a clean interface.
-  </para>
-
- <para>
-   There are five methods that an index operator class for
-   <acronym>GiST</acronym> must provide, and seven that are optional.
-   Correctness of the index is ensured
-   by proper implementation of the <function>same</function>, <function>consistent</function>
-   and <function>union</function> methods, while efficiency (size and speed) of the
-   index will depend on the <function>penalty</function> and <function>picksplit</function>
-   methods.
-   Two optional methods are <function>compress</function> and
-   <function>decompress</function>, which allow an index to have internal tree data of
-   a different type than the data it indexes. The leaves are to be of the
-   indexed data type, while the other tree nodes can be of any C struct (but
-   you still have to follow <productname>PostgreSQL</productname> data type rules here,
-   see about <literal>varlena</literal> for variable sized data). If the tree's
-   internal data type exists at the SQL level, the <literal>STORAGE</literal> option
-   of the <command>CREATE OPERATOR CLASS</command> command can be used.
-   The optional eighth method is <function>distance</function>, which is needed
-   if the operator class wishes to support ordered scans (nearest-neighbor
-   searches). The optional ninth method <function>fetch</function> is needed if the
-   operator class wishes to support index-only scans, except when the
-   <function>compress</function> method is omitted. The optional tenth method
-   <function>options</function> is needed if the operator class has
-   user-specified parameters.
-   The optional eleventh method <function>sortsupport</function> is used to
-   speed up building a <acronym>GiST</acronym> index.
-   The optional twelfth method <function>stratnum</function> is used to
-   translate well-known <literal>RT*StrategyNumber</literal>s (from
-   <filename>src/include/access/stratnum.h</filename>) into strategy numbers
-   used by the operator class.  This lets the core code look up operators for
-   temporal constraint indexes.
- </para>
-
- <variablelist>
-    <varlistentry>
-     <term><function>consistent</function></term>
-     <listitem>
-      <para>
-       Given an index entry <literal>p</literal> and a query value <literal>q</literal>,
-       this function determines whether the index entry is
-       <quote>consistent</quote> with the query; that is, could the predicate
-       <quote><replaceable>indexed_column</replaceable>
-       <replaceable>indexable_operator</replaceable> <literal>q</literal></quote> be true for
-       any row represented by the index entry?  For a leaf index entry this is
-       equivalent to testing the indexable condition, while for an internal
-       tree node this determines whether it is necessary to scan the subtree
-       of the index represented by the tree node.  When the result is
-       <literal>true</literal>, a <literal>recheck</literal> flag must also be returned.
-       This indicates whether the predicate is certainly true or only possibly
-       true.  If <literal>recheck</literal> = <literal>false</literal> then the index has
-       tested the predicate condition exactly, whereas if <literal>recheck</literal>
-       = <literal>true</literal> the row is only a candidate match.  In that case the
-       system will automatically evaluate the
-       <replaceable>indexable_operator</replaceable> against the actual row value to see
-       if it is really a match.  This convention allows
-       <acronym>GiST</acronym> to support both lossless and lossy index
-       structures.
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_consistent(internal, data_type, smallint, oid, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-
-        And the matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_consistent);
-
-Datum
-my_consistent(PG_FUNCTION_ARGS)
-{
-    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
-    data_type  *query = PG_GETARG_DATA_TYPE_P(1);
-    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
-    /* Oid subtype = PG_GETARG_OID(3); */
-    bool       *recheck = (bool *) PG_GETARG_POINTER(4);
-    data_type  *key = DatumGetDataType(entry-&gt;key);
-    bool        retval;
-
-    /*
-     * determine return value as a function of strategy, key and query.
-     *
-     * Use GIST_LEAF(entry) to know where you're called in the index tree,
-     * which comes handy when supporting the = operator for example (you could
-     * check for non empty union() in non-leaf nodes and equality in leaf
-     * nodes).
-     */
-
-    *recheck = true;        /* or false if check is exact */
-
-    PG_RETURN_BOOL(retval);
-}
-</programlisting>
-
-       Here, <varname>key</varname> is an element in the index and <varname>query</varname>
-       the value being looked up in the index. The <literal>StrategyNumber</literal>
-       parameter indicates which operator of your operator class is being
-       applied &mdash; it matches one of the operator numbers in the
-       <command>CREATE OPERATOR CLASS</command> command.
-      </para>
-
-      <para>
-       Depending on which operators you have included in the class, the data
-       type of <varname>query</varname> could vary with the operator, since it will
-       be whatever type is on the right-hand side of the operator, which might
-       be different from the indexed data type appearing on the left-hand side.
-       (The above code skeleton assumes that only one type is possible; if
-       not, fetching the <varname>query</varname> argument value would have to depend
-       on the operator.)  It is recommended that the SQL declaration of
-       the <function>consistent</function> function use the opclass's indexed data
-       type for the <varname>query</varname> argument, even though the actual type
-       might be something else depending on the operator.
-      </para>
-
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>union</function></term>
-     <listitem>
-      <para>
-       This method consolidates information in the tree.  Given a set of
-       entries, this function generates a new index entry that represents
-       all the given entries.
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_union(internal, internal)
-RETURNS storage_type
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-
-        And the matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_union);
-
-Datum
-my_union(PG_FUNCTION_ARGS)
-{
-    GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
-    GISTENTRY  *ent = entryvec-&gt;vector;
-    data_type  *out,
-               *tmp,
-               *old;
-    int         numranges,
-                i = 0;
-
-    numranges = entryvec-&gt;n;
-    tmp = DatumGetDataType(ent[0].key);
-    out = tmp;
-
-    if (numranges == 1)
-    {
-        out = data_type_deep_copy(tmp);
-
-        PG_RETURN_DATA_TYPE_P(out);
-    }
-
-    for (i = 1; i &lt; numranges; i++)
-    {
-        old = out;
-        tmp = DatumGetDataType(ent[i].key);
-        out = my_union_implementation(out, tmp);
-    }
-
-    PG_RETURN_DATA_TYPE_P(out);
-}
-</programlisting>
-      </para>
-
-      <para>
-        As you can see, in this skeleton we're dealing with a data type
-        where <literal>union(X, Y, Z) = union(union(X, Y), Z)</literal>. It's easy
-        enough to support data types where this is not the case, by
-        implementing the proper union algorithm in this
-        <acronym>GiST</acronym> support method.
-      </para>
-
-      <para>
-        The result of the <function>union</function> function must be a value of the
-        index's storage type, whatever that is (it might or might not be
-        different from the indexed column's type).  The <function>union</function>
-        function should return a pointer to newly <function>palloc()</function>ed
-        memory. You can't just return the input value as-is, even if there is
-        no type change.
-      </para>
-
-      <para>
-       As shown above, the <function>union</function> function's
-       first <type>internal</type> argument is actually
-       a <structname>GistEntryVector</structname> pointer.  The second argument is a
-       pointer to an integer variable, which can be ignored.  (It used to be
-       required that the <function>union</function> function store the size of its
-       result value into that variable, but this is no longer necessary.)
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>compress</function></term>
-     <listitem>
-      <para>
-       Converts a data item into a format suitable for physical storage in
-       an index page.
-       If the <function>compress</function> method is omitted, data items are stored
-       in the index without modification.
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-
-        And the matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_compress);
-
-Datum
-my_compress(PG_FUNCTION_ARGS)
-{
-    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
-    GISTENTRY  *retval;
-
-    if (entry-&gt;leafkey)
-    {
-        /* replace entry-&gt;key with a compressed version */
-        compressed_data_type *compressed_data = palloc(sizeof(compressed_data_type));
-
-        /* fill *compressed_data from entry-&gt;key ... */
-
-        retval = palloc(sizeof(GISTENTRY));
-        gistentryinit(*retval, PointerGetDatum(compressed_data),
-                      entry-&gt;rel, entry-&gt;page, entry-&gt;offset, FALSE);
-    }
-    else
-    {
-        /* typically we needn't do anything with non-leaf entries */
-        retval = entry;
-    }
-
-    PG_RETURN_POINTER(retval);
-}
-</programlisting>
-      </para>
-
-      <para>
-       You have to adapt <replaceable>compressed_data_type</replaceable> to the specific
-       type you're converting to in order to compress your leaf nodes, of
-       course.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>decompress</function></term>
-     <listitem>
-      <para>
-       Converts the stored representation of a data item into a format that
-       can be manipulated by the other GiST methods in the operator class.
-       If the <function>decompress</function> method is omitted, it is assumed that
-       the other GiST methods can work directly on the stored data format.
-       (<function>decompress</function> is not necessarily the reverse of
-       the <function>compress</function> method; in particular,
-       if <function>compress</function> is lossy then it's impossible
-       for <function>decompress</function> to exactly reconstruct the original
-       data.  <function>decompress</function> is not necessarily equivalent
-       to <function>fetch</function>, either, since the other GiST methods might not
-       require full reconstruction of the data.)
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-
-        And the matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_decompress);
-
-Datum
-my_decompress(PG_FUNCTION_ARGS)
-{
-    PG_RETURN_POINTER(PG_GETARG_POINTER(0));
-}
-</programlisting>
-
-        The above skeleton is suitable for the case where no decompression
-        is needed.  (But, of course, omitting the method altogether is even
-        easier, and is recommended in such cases.)
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>penalty</function></term>
-     <listitem>
-      <para>
-       Returns a value indicating the <quote>cost</quote> of inserting the new
-       entry into a particular branch of the tree.  Items will be inserted
-       down the path of least <function>penalty</function> in the tree.
-       Values returned by <function>penalty</function> should be non-negative.
-       If a negative value is returned, it will be treated as zero.
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_penalty(internal, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;  -- in some cases penalty functions need not be strict
-</programlisting>
-
-        And the matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_penalty);
-
-Datum
-my_penalty(PG_FUNCTION_ARGS)
-{
-    GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
-    GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
-    float      *penalty = (float *) PG_GETARG_POINTER(2);
-    data_type  *orig = DatumGetDataType(origentry-&gt;key);
-    data_type  *new = DatumGetDataType(newentry-&gt;key);
-
-    *penalty = my_penalty_implementation(orig, new);
-    PG_RETURN_POINTER(penalty);
-}
-</programlisting>
-
-        For historical reasons, the <function>penalty</function> function doesn't
-        just return a <type>float</type> result; instead it has to store the value
-        at the location indicated by the third argument.  The return
-        value per se is ignored, though it's conventional to pass back the
-        address of that argument.
-      </para>
-
-      <para>
-        The <function>penalty</function> function is crucial to good performance of
-        the index. It'll get used at insertion time to determine which branch
-        to follow when choosing where to add the new entry in the tree. At
-        query time, the more balanced the index, the quicker the lookup.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>picksplit</function></term>
-     <listitem>
-      <para>
-       When an index page split is necessary, this function decides which
-       entries on the page are to stay on the old page, and which are to move
-       to the new page.
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-
-        And the matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_picksplit);
-
-Datum
-my_picksplit(PG_FUNCTION_ARGS)
-{
-    GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
-    GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
-    OffsetNumber maxoff = entryvec-&gt;n - 1;
-    GISTENTRY  *ent = entryvec-&gt;vector;
-    int         i,
-                nbytes;
-    OffsetNumber *left,
-               *right;
-    data_type  *tmp_union;
-    data_type  *unionL;
-    data_type  *unionR;
-    GISTENTRY **raw_entryvec;
-
-    maxoff = entryvec-&gt;n - 1;
-    nbytes = (maxoff + 1) * sizeof(OffsetNumber);
-
-    v-&gt;spl_left = (OffsetNumber *) palloc(nbytes);
-    left = v-&gt;spl_left;
-    v-&gt;spl_nleft = 0;
-
-    v-&gt;spl_right = (OffsetNumber *) palloc(nbytes);
-    right = v-&gt;spl_right;
-    v-&gt;spl_nright = 0;
-
-    unionL = NULL;
-    unionR = NULL;
-
-    /* Initialize the raw entry vector. */
-    raw_entryvec = (GISTENTRY **) malloc(entryvec-&gt;n * sizeof(void *));
-    for (i = FirstOffsetNumber; i &lt;= maxoff; i = OffsetNumberNext(i))
-        raw_entryvec[i] = &amp;(entryvec-&gt;vector[i]);
-
-    for (i = FirstOffsetNumber; i &lt;= maxoff; i = OffsetNumberNext(i))
-    {
-        int         real_index = raw_entryvec[i] - entryvec-&gt;vector;
-
-        tmp_union = DatumGetDataType(entryvec-&gt;vector[real_index].key);
-        Assert(tmp_union != NULL);
-
-        /*
-         * Choose where to put the index entries and update unionL and unionR
-         * accordingly. Append the entries to either v-&gt;spl_left or
-         * v-&gt;spl_right, and care about the counters.
-         */
-
-        if (my_choice_is_left(unionL, curl, unionR, curr))
-        {
-            if (unionL == NULL)
-                unionL = tmp_union;
-            else
-                unionL = my_union_implementation(unionL, tmp_union);
-
-            *left = real_index;
-            ++left;
-            ++(v-&gt;spl_nleft);
-        }
-        else
-        {
-            /*
-             * Same on the right
-             */
-        }
-    }
-
-    v-&gt;spl_ldatum = DataTypeGetDatum(unionL);
-    v-&gt;spl_rdatum = DataTypeGetDatum(unionR);
-    PG_RETURN_POINTER(v);
-}
-</programlisting>
-
-       Notice that the <function>picksplit</function> function's result is delivered
-       by modifying the passed-in <structname>v</structname> structure.  The return
-       value per se is ignored, though it's conventional to pass back the
-       address of <structname>v</structname>.
-      </para>
-
-      <para>
-        Like <function>penalty</function>, the <function>picksplit</function> function
-        is crucial to good performance of the index.  Designing suitable
-        <function>penalty</function> and <function>picksplit</function> implementations
-        is where the challenge of implementing well-performing
-        <acronym>GiST</acronym> indexes lies.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>same</function></term>
-     <listitem>
-      <para>
-       Returns true if two index entries are identical, false otherwise.
-       (An <quote>index entry</quote> is a value of the index's storage type,
-       not necessarily the original indexed column's type.)
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_same(storage_type, storage_type, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-
-        And the matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_same);
-
-Datum
-my_same(PG_FUNCTION_ARGS)
-{
-    prefix_range *v1 = PG_GETARG_PREFIX_RANGE_P(0);
-    prefix_range *v2 = PG_GETARG_PREFIX_RANGE_P(1);
-    bool       *result = (bool *) PG_GETARG_POINTER(2);
-
-    *result = my_eq(v1, v2);
-    PG_RETURN_POINTER(result);
-}
-</programlisting>
-
-        For historical reasons, the <function>same</function> function doesn't
-        just return a Boolean result; instead it has to store the flag
-        at the location indicated by the third argument.  The return
-        value per se is ignored, though it's conventional to pass back the
-        address of that argument.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>distance</function></term>
-     <listitem>
-      <para>
-       Given an index entry <literal>p</literal> and a query value <literal>q</literal>,
-       this function determines the index entry's
-       <quote>distance</quote> from the query value.  This function must be
-       supplied if the operator class contains any ordering operators.
-       A query using the ordering operator will be implemented by returning
-       index entries with the smallest <quote>distance</quote> values first,
-       so the results must be consistent with the operator's semantics.
-       For a leaf index entry the result just represents the distance to
-       the index entry; for an internal tree node, the result must be the
-       smallest distance that any child entry could have.
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-
-        And the matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_distance);
-
-Datum
-my_distance(PG_FUNCTION_ARGS)
-{
-    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
-    data_type  *query = PG_GETARG_DATA_TYPE_P(1);
-    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
-    /* Oid subtype = PG_GETARG_OID(3); */
-    /* bool *recheck = (bool *) PG_GETARG_POINTER(4); */
-    data_type  *key = DatumGetDataType(entry-&gt;key);
-    double      retval;
-
-    /*
-     * determine return value as a function of strategy, key and query.
-     */
-
-    PG_RETURN_FLOAT8(retval);
-}
-</programlisting>
-
-       The arguments to the <function>distance</function> function are identical to
-       the arguments of the <function>consistent</function> function.
-      </para>
-
-      <para>
-       Some approximation is allowed when determining the distance, so long
-       as the result is never greater than the entry's actual distance. Thus,
-       for example, distance to a bounding box is usually sufficient in
-       geometric applications.  For an internal tree node, the distance
-       returned must not be greater than the distance to any of the child
-       nodes. If the returned distance is not exact, the function must set
-       <literal>*recheck</literal> to true. (This is not necessary for internal tree
-       nodes; for them, the calculation is always assumed to be inexact.) In
-       this case the executor will calculate the accurate distance after
-       fetching the tuple from the heap, and reorder the tuples if necessary.
-      </para>
-
-      <para>
-       If the distance function returns <literal>*recheck = true</literal> for any
-       leaf node, the original ordering operator's return type must
-       be <type>float8</type> or <type>float4</type>, and the distance function's
-       result values must be comparable to those of the original ordering
-       operator, since the executor will sort using both distance function
-       results and recalculated ordering-operator results.  Otherwise, the
-       distance function's result values can be any finite <type>float8</type>
-       values, so long as the relative order of the result values matches the
-       order returned by the ordering operator.  (Infinity and minus infinity
-       are used internally to handle cases such as nulls, so it is not
-       recommended that <function>distance</function> functions return these values.)
-      </para>
-
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>fetch</function></term>
-     <listitem>
-      <para>
-       Converts the compressed index representation of a data item into the
-       original data type, for index-only scans. The returned data must be an
-       exact, non-lossy copy of the originally indexed value.
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-
-        The argument is a pointer to a <structname>GISTENTRY</structname> struct. On
-        entry, its <structfield>key</structfield> field contains a non-NULL leaf datum in
-        compressed form. The return value is another <structname>GISTENTRY</structname>
-        struct, whose <structfield>key</structfield> field contains the same datum in its
-        original, uncompressed form. If the opclass's compress function does
-        nothing for leaf entries, the <function>fetch</function> method can return the
-        argument as-is.  Or, if the opclass does not have a compress function,
-        the <function>fetch</function> method can be omitted as well, since it would
-        necessarily be a no-op.
-       </para>
-
-       <para>
-        The matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_fetch);
-
-Datum
-my_fetch(PG_FUNCTION_ARGS)
-{
-    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
-    input_data_type *in = DatumGetPointer(entry->key);
-    fetched_data_type *fetched_data;
-    GISTENTRY  *retval;
-
-    retval = palloc(sizeof(GISTENTRY));
-    fetched_data = palloc(sizeof(fetched_data_type));
-
-    /*
-     * Convert 'fetched_data' into the a Datum of the original datatype.
-     */
-
-    /* fill *retval from fetched_data. */
-    gistentryinit(*retval, PointerGetDatum(converted_datum),
-                  entry->rel, entry->page, entry->offset, FALSE);
-
-    PG_RETURN_POINTER(retval);
-}
-</programlisting>
-      </para>
-
-      <para>
-       If the compress method is lossy for leaf entries, the operator class
-       cannot support index-only scans, and must not define
-       a <function>fetch</function> function.
-      </para>
-
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>options</function></term>
-     <listitem>
-      <para>
-       Allows definition of user-visible parameters that control operator
-       class behavior.
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_options(internal)
-RETURNS void
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-      </para>
-
-      <para>
-       The function is passed a pointer to a <structname>local_relopts</structname>
-       struct, which needs to be filled with a set of operator class
-       specific options.  The options can be accessed from other support
-       functions using the <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
-       <literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
-      </para>
-
-       <para>
-        An example implementation of my_options() and parameters use
-        from other support functions are given below:
-
-<programlisting>
-typedef enum MyEnumType
-{
-    MY_ENUM_ON,
-    MY_ENUM_OFF,
-    MY_ENUM_AUTO
-} MyEnumType;
-
-typedef struct
-{
-    int32   vl_len_;    /* varlena header (do not touch directly!) */
-    int     int_param;  /* integer parameter */
-    double  real_param; /* real parameter */
-    MyEnumType enum_param; /* enum parameter */
-    int     str_param;  /* string parameter */
-} MyOptionsStruct;
-
-/* String representation of enum values */
-static relopt_enum_elt_def myEnumValues[] =
-{
-    {"on", MY_ENUM_ON},
-    {"off", MY_ENUM_OFF},
-    {"auto", MY_ENUM_AUTO},
-    {(const char *) NULL}   /* list terminator */
-};
-
-static char *str_param_default = "default";
-
-/*
- * Sample validator: checks that string is not longer than 8 bytes.
- */
-static void
-validate_my_string_relopt(const char *value)
-{
-    if (strlen(value) > 8)
-        ereport(ERROR,
-                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                 errmsg("str_param must be at most 8 bytes")));
-}
-
-/*
- * Sample filler: switches characters to lower case.
- */
-static Size
-fill_my_string_relopt(const char *value, void *ptr)
-{
-    char   *tmp = str_tolower(value, strlen(value), DEFAULT_COLLATION_OID);
-    int     len = strlen(tmp);
-
-    if (ptr)
-        strcpy((char *) ptr, tmp);
-
-    pfree(tmp);
-    return len + 1;
-}
-
-PG_FUNCTION_INFO_V1(my_options);
-
-Datum
-my_options(PG_FUNCTION_ARGS)
-{
-    local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
-
-    init_local_reloptions(relopts, sizeof(MyOptionsStruct));
-    add_local_int_reloption(relopts, "int_param", "integer parameter",
-                            100, 0, 1000000,
-                            offsetof(MyOptionsStruct, int_param));
-    add_local_real_reloption(relopts, "real_param", "real parameter",
-                             1.0, 0.0, 1000000.0,
-                             offsetof(MyOptionsStruct, real_param));
-    add_local_enum_reloption(relopts, "enum_param", "enum parameter",
-                             myEnumValues, MY_ENUM_ON,
-                             "Valid values are: \"on\", \"off\" and \"auto\".",
-                             offsetof(MyOptionsStruct, enum_param));
-    add_local_string_reloption(relopts, "str_param", "string parameter",
-                               str_param_default,
-                               &amp;validate_my_string_relopt,
-                               &amp;fill_my_string_relopt,
-                               offsetof(MyOptionsStruct, str_param));
-
-    PG_RETURN_VOID();
-}
-
-PG_FUNCTION_INFO_V1(my_compress);
-
-Datum
-my_compress(PG_FUNCTION_ARGS)
-{
-    int     int_param = 100;
-    double  real_param = 1.0;
-    MyEnumType enum_param = MY_ENUM_ON;
-    char   *str_param = str_param_default;
-
-    /*
-     * Normally, when opclass contains 'options' method, then options are always
-     * passed to support functions.  However, if you add 'options' method to
-     * existing opclass, previously defined indexes have no options, so the
-     * check is required.
-     */
-    if (PG_HAS_OPCLASS_OPTIONS())
-    {
-        MyOptionsStruct *options = (MyOptionsStruct *) PG_GET_OPCLASS_OPTIONS();
-
-        int_param = options->int_param;
-        real_param = options->real_param;
-        enum_param = options->enum_param;
-        str_param = GET_STRING_RELOPTION(options, str_param);
-    }
-
-    /* the rest implementation of support function */
-}
-
-</programlisting>
-      </para>
-
-      <para>
-       Since the representation of the key in <acronym>GiST</acronym> is
-       flexible, it may depend on user-specified parameters.  For instance,
-       the length of key signature may be specified.  See
-       <literal>gtsvector_options()</literal> for example.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>sortsupport</function></term>
-     <listitem>
-      <para>
-       Returns a comparator function to sort data in a way that preserves
-       locality. It is used by <command>CREATE INDEX</command> and
-       <command>REINDEX</command> commands. The quality of the created index
-       depends on how well the sort order determined by the comparator function
-       preserves locality of the inputs.
-      </para>
-      <para>
-       The <function>sortsupport</function> method is optional. If it is not
-       provided, <command>CREATE INDEX</command> builds the index by inserting
-       each tuple to the tree using the <function>penalty</function> and
-       <function>picksplit</function> functions, which is much slower.
-      </para>
-
-      <para>
-       The <acronym>SQL</acronym> declaration of the function must look like
-       this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_sortsupport(internal)
-RETURNS void
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-
-       The argument is a pointer to a <structname>SortSupport</structname>
-       struct. At a minimum, the function must fill in its comparator field.
-       The comparator takes three arguments: two Datums to compare, and
-       a pointer to the <structname>SortSupport</structname> struct. The
-       Datums are the two indexed values in the format that they are stored
-       in the index; that is, in the format returned by the
-       <function>compress</function> method. The full API is defined in
-       <filename>src/include/utils/sortsupport.h</filename>.
-       </para>
-
-       <para>
-        The matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_sortsupport);
-
-static int
-my_fastcmp(Datum x, Datum y, SortSupport ssup)
-{
-  /* establish order between x and y by computing some sorting value z */
-
-  int z1 = ComputeSpatialCode(x);
-  int z2 = ComputeSpatialCode(y);
-
-  return z1 == z2 ? 0 : z1 > z2 ? 1 : -1;
-}
-
-Datum
-my_sortsupport(PG_FUNCTION_ARGS)
-{
-  SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
-
-  ssup->comparator = my_fastcmp;
-  PG_RETURN_VOID();
-}
-</programlisting>
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>stratnum</function></term>
-     <listitem>
-      <para>
-       Given an <literal>RT*StrategyNumber</literal> value from
-       <filename>src/include/access/stratnum.h</filename>, returns a strategy
-       number used by this operator class for matching functionality.  The
-       function should return <literal>InvalidStrategy</literal> if the
-       operator class has no matching strategy.
-      </para>
-
-      <para>
-       This is used for temporal index constraints (i.e., <literal>PRIMARY
-       KEY</literal> and <literal>UNIQUE</literal>).  If the operator class
-       provides this function and it returns results for
-       <literal>RTEqualStrategyNumber</literal>, it can be used in the
-       non-<literal>WITHOUT OVERLAPS</literal> part(s) of an index constraint.
-       If it returns results for <literal>RTOverlapStrategyNumber</literal>,
-       the operator class can be used in the <literal>WITHOUT
-       OVERLAPS</literal> part of an index constraint.
-      </para>
-
-      <para>
-       The <acronym>SQL</acronym> declaration of the function must look like
-       this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_stratnum(integer)
-RETURNS integer
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-      </para>
-
-       <para>
-        The matching code in the C module could then follow this skeleton:
-
-<programlisting>
-PG_FUNCTION_INFO_V1(my_stratnum);
-
-Datum
-my_stratnum(PG_FUNCTION_ARGS)
-{
-    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(1);
-    StrategyNumber ret = InvalidStrategy;
-
-    switch (strategy)
-    {
-        case RTEqualStrategyNumber:
-            ret = BTEqualStrategyNumber;
-    }
-
-    PG_RETURN_UINT16(ret);
-}
-</programlisting>
-      </para>
-
-      <para>
-       One translation function is provided by
-       <productname>PostgreSQL</productname>:
-       <literal>gist_stratnum_identity</literal> is for operator classes that
-       already use the <literal>RT*StrategyNumber</literal> constants.  It
-       returns whatever is passed to it.  The <literal>btree_gist</literal>
-       extension defines a second translation function,
-       <literal>gist_stratnum_btree</literal>, for operator classes that use
-       the <literal>BT*StrategyNumber</literal> constants.
-      </para>
-     </listitem>
-    </varlistentry>
-  </variablelist>
-
-  <para>
-   All the GiST support methods are normally called in short-lived memory
-   contexts; that is, <varname>CurrentMemoryContext</varname> will get reset after
-   each tuple is processed.  It is therefore not very important to worry about
-   pfree'ing everything you palloc.  However, in some cases it's useful for a
-   support method to cache data across repeated calls.  To do that, allocate
-   the longer-lived data in <literal>fcinfo-&gt;flinfo-&gt;fn_mcxt</literal>, and
-   keep a pointer to it in <literal>fcinfo-&gt;flinfo-&gt;fn_extra</literal>.  Such
-   data will survive for the life of the index operation (e.g., a single GiST
-   index scan, index build, or index tuple insertion).  Be careful to pfree
-   the previous value when replacing a <literal>fn_extra</literal> value, or the leak
-   will accumulate for the duration of the operation.
-  </para>
-
-</sect1>
-
-<sect1 id="gist-implementation">
- <title>Implementation</title>
-
- <sect2 id="gist-buffering-build">
-  <title>GiST Index Build Methods</title>
-
-  <para>
-   The simplest way to build a GiST index is just to insert all the entries,
-   one by one.  This tends to be slow for large indexes, because if the
-   index tuples are scattered across the index and the index is large enough
-   to not fit in cache, a lot of random I/O will be
-   needed.  <productname>PostgreSQL</productname> supports two alternative
-   methods for initial build of a GiST index: <firstterm>sorted</firstterm>
-   and <firstterm>buffered</firstterm> modes.
-  </para>
-
-  <para>
-   The sorted method is only available if each of the opclasses used by the
-   index provides a <function>sortsupport</function> function, as described
-   in <xref linkend="gist-extensibility"/>.  If they do, this method is
-   usually the best, so it is used by default.
-  </para>
-
-  <para>
-   The buffered method works by not inserting tuples directly into the index
-   right away.  It can dramatically reduce the amount of random I/O needed
-   for non-ordered data sets.  For well-ordered data sets the benefit is
-   smaller or non-existent, because only a small number of pages receive new
-   tuples at a time, and those pages fit in cache even if the index as a
-   whole does not.
-  </para>
-
-  <para>
-   The buffered method needs to call the <function>penalty</function>
-   function more often than the simple method does, which consumes some
-   extra CPU resources. Also, the buffers need temporary disk space, up to
-   the size of the resulting index. Buffering can also influence the quality
-   of the resulting index, in both positive and negative directions. That
-   influence depends on various factors, like the distribution of the input
-   data and the operator class implementation.
-  </para>
-
-  <para>
-   If sorting is not possible, then by default a GiST index build switches
-   to the buffering method when the index size reaches
-   <xref linkend="guc-effective-cache-size"/>.  Buffering can be manually
-   forced or prevented by the <literal>buffering</literal> parameter to the
-   CREATE INDEX command.  The default behavior is good for most cases, but
-   turning buffering off might speed up the build somewhat if the input data
-   is ordered.
-  </para>
-
- </sect2>
-</sect1>
-
-<sect1 id="gist-examples">
- <title>Examples</title>
-
- <para>
-  The <productname>PostgreSQL</productname> source distribution includes
-  several examples of index methods implemented using
-  <acronym>GiST</acronym>.  The core system currently provides text search
-  support (indexing for <type>tsvector</type> and <type>tsquery</type>) as well as
-  R-Tree equivalent functionality for some of the built-in geometric data types
-  (see <filename>src/backend/access/gist/gistproc.c</filename>).  The following
-  <filename>contrib</filename> modules also contain <acronym>GiST</acronym>
-  operator classes:
-
- <variablelist>
-  <varlistentry>
-   <term><filename>btree_gist</filename></term>
-   <listitem>
-    <para>B-tree equivalent functionality for several data types</para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><filename>cube</filename></term>
-   <listitem>
-    <para>Indexing for multidimensional cubes</para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><filename>hstore</filename></term>
-   <listitem>
-    <para>Module for storing (key, value) pairs</para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><filename>intarray</filename></term>
-   <listitem>
-    <para>RD-Tree for one-dimensional array of int4 values</para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><filename>ltree</filename></term>
-   <listitem>
-    <para>Indexing for tree-like structures</para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><filename>pg_trgm</filename></term>
-   <listitem>
-    <para>Text similarity using trigram matching</para>
-   </listitem>
-  </varlistentry>
-
-  <varlistentry>
-   <term><filename>seg</filename></term>
-   <listitem>
-    <para>Indexing for <quote>float ranges</quote></para>
-   </listitem>
-  </varlistentry>
- </variablelist>
- </para>
-
-</sect1>
-
-</chapter>
diff --git a/doc/src/sgml/hash.sgml b/doc/src/sgml/hash.sgml
deleted file mode 100644
index e35911ebf8..0000000000
--- a/doc/src/sgml/hash.sgml
+++ /dev/null
@@ -1,162 +0,0 @@
-<!-- doc/src/sgml/hash.sgml -->
-
-<chapter id="hash-index">
-<title>Hash Indexes</title>
-
-   <indexterm>
-    <primary>index</primary>
-    <secondary>Hash</secondary>
-   </indexterm>
-
-<sect1 id="hash-intro">
- <title>Overview</title>
-
- <para>
-  <productname>PostgreSQL</productname>
-  includes an implementation of persistent on-disk hash indexes,
-  which are fully crash recoverable. Any data type can be indexed by a
-  hash index, including data types that do not have a well-defined linear
-  ordering. Hash indexes store only the hash value of the data being
-  indexed, thus there are no restrictions on the size of the data column
-  being indexed.
- </para>
-
- <para>
-  Hash indexes support only single-column indexes and do not allow
-  uniqueness checking.
- </para>
-
- <para>
-  Hash indexes support only the <literal>=</literal> operator,
-  so WHERE clauses that specify range operations will not be able to take
-  advantage of hash indexes.
- </para>
-
- <para>
-  Each hash index tuple stores just the 4-byte hash value, not the actual
-  column value. As a result, hash indexes may be much smaller than B-trees
-  when indexing longer data items such as UUIDs, URLs, etc. The absence of
-  the column value also makes all hash index scans lossy. Hash indexes may
-  take part in bitmap index scans and backward scans.
- </para>
-
- <para>
-  Hash indexes are best optimized for SELECT and UPDATE-heavy workloads
-  that use equality scans on larger tables. In a B-tree index, searches must
-  descend through the tree until the leaf page is found. In tables with
-  millions of rows, this descent can increase access time to data. The
-  equivalent of a leaf page in a hash index is referred to as a bucket page. In
-  contrast, a hash index allows accessing the bucket pages directly,
-  thereby potentially reducing index access time in larger tables. This
-  reduction in "logical I/O" becomes even more pronounced on indexes/data
-  larger than shared_buffers/RAM.
- </para>
-
- <para>
-  Hash indexes have been designed to cope with uneven distributions of
-  hash values. Direct access to the bucket pages works well if the hash
-  values are evenly distributed. When inserts mean that the bucket page
-  becomes full, additional overflow pages are chained to that specific
-  bucket page, locally expanding the storage for index tuples that match
-  that hash value. When scanning a hash bucket during queries, we need to
-  scan through all of the overflow pages. Thus an unbalanced hash index
-  might actually be worse than a B-tree in terms of number of block
-  accesses required, for some data.
- </para>
-
- <para>
-  As a result of the overflow cases, we can say that hash indexes are
-  most suitable for unique, nearly unique data or data with a low number
-  of rows per hash bucket.
-  One possible way to avoid problems is to exclude highly non-unique
-  values from the index using a partial index condition, but this may
-  not be suitable in many cases.
- </para>
-
- <para>
-  Like B-Trees, hash indexes perform simple index tuple deletion. This
-  is a deferred maintenance operation that deletes index tuples that are
-  known to be safe to delete (those whose item identifier's LP_DEAD bit
-  is already set). If an insert finds no space is available on a page we
-  try to avoid creating a new overflow page by attempting to remove dead
-  index tuples. Removal cannot occur if the page is pinned at that time.
-  Deletion of dead index pointers also occurs during VACUUM.
- </para>
-
- <para>
-  If it can, VACUUM will also try to squeeze the index tuples onto as
-  few overflow pages as possible, minimizing the overflow chain. If an
-  overflow page becomes empty, overflow pages can be recycled for reuse
-  in other buckets, though we never return them to the operating system.
-  There is currently no provision to shrink a hash index, other than by
-  rebuilding it with REINDEX.
-  There is no provision for reducing the number of buckets, either.
- </para>
-
- <para>
-  Hash indexes may expand the number of bucket pages as the number of
-  rows indexed grows. The hash key-to-bucket-number mapping is chosen so that
-  the index can be incrementally expanded. When a new bucket is to be added to
-  the index, exactly one existing bucket will need to be "split", with some of
-  its tuples being transferred to the new bucket according to the updated
-  key-to-bucket-number mapping.
- </para>
-
- <para>
-  The expansion occurs in the foreground, which could increase execution
-  time for user inserts. Thus, hash indexes may not be suitable for tables
-  with rapidly increasing number of rows.
- </para>
-
-</sect1>
-
-<sect1 id="hash-implementation">
- <title>Implementation</title>
-
- <para>
-  There are four kinds of pages in a hash index: the meta page (page zero),
-  which contains statically allocated control information; primary bucket
-  pages; overflow pages; and bitmap pages, which keep track of overflow
-  pages that have been freed and are available for re-use. For addressing
-  purposes, bitmap pages are regarded as a subset of the overflow pages.
- </para>
-
- <para>
-  Both scanning the index and inserting tuples require locating the bucket
-  where a given tuple ought to be located. To do this, we need the bucket
-  count, highmask, and lowmask from the metapage; however, it's undesirable
-  for performance reasons to have to have to lock and pin the metapage for
-  every such operation. Instead, we retain a cached copy of the metapage
-  in each backend's relcache entry. This will produce the correct bucket
-  mapping as long as the target bucket hasn't been split since the last
-  cache refresh.
- </para>
-
- <para>
-  Primary bucket pages and overflow pages are allocated independently since
-  any given index might need more or fewer overflow pages relative to its
-  number of buckets. The hash code uses an interesting set of addressing
-  rules to support a variable number of overflow pages while not having to
-  move primary bucket pages around after they are created.
- </para>
-
- <para>
-  Each row in the table indexed is represented by a single index tuple in
-  the hash index. Hash index tuples are stored in bucket pages, and if
-  they exist, overflow pages. We speed up searches by keeping the index entries
-  in any one index page sorted by hash code, thus allowing binary search to be
-  used within an index page. Note however that there is *no* assumption about
-  the relative ordering of hash codes across different index pages of a bucket.
- </para>
-
- <para>
-  The bucket splitting algorithms to expand the hash index are too complex to
-  be worthy of mention here, though are described in more detail in
-  <filename>src/backend/access/hash/README</filename>.
-  The split algorithm is crash safe and can be restarted if not completed
-  successfully.
- </para>
-
-</sect1>
-
-</chapter>
diff --git a/doc/src/sgml/indextypes.sgml b/doc/src/sgml/indextypes.sgml
new file mode 100644
index 0000000000..7aad8d94ed
--- /dev/null
+++ b/doc/src/sgml/indextypes.sgml
@@ -0,0 +1,5597 @@
+ <!-- doc/src/sgml/indextypes.sgml -->
+
+<chapter id="indextypes">
+<title>Built-in Index Access Methods</title>
+
+ <sect1 id="btree">
+ <title>B-Tree Indexes</title>
+
+    <indexterm>
+     <primary>index</primary>
+     <secondary>B-Tree</secondary>
+    </indexterm>
+
+ <sect2 id="btree-intro">
+  <title>Introduction</title>
+
+  <para>
+   <productname>PostgreSQL</productname> includes an implementation of the
+   standard <acronym>btree</acronym> (multi-way balanced tree) index data
+   structure.  Any data type that can be sorted into a well-defined linear
+   order can be indexed by a btree index.  The only limitation is that an
+   index entry cannot exceed approximately one-third of a page (after TOAST
+   compression, if applicable).
+  </para>
+
+  <para>
+   Because each btree operator class imposes a sort order on its data type,
+   btree operator classes (or, really, operator families) have come to be
+   used as <productname>PostgreSQL</productname>'s general representation
+   and understanding of sorting semantics.  Therefore, they've acquired
+   some features that go beyond what would be needed just to support btree
+   indexes, and parts of the system that are quite distant from the
+   btree <acronym>AM</acronym> make use of them.
+  </para>
+
+ </sect2>
+
+ <sect2 id="btree-behavior">
+  <title>Behavior of B-Tree Operator Classes</title>
+
+  <para>
+   As shown in <xref linkend="xindex-btree-strat-table"/>, a btree operator
+   class must provide five comparison operators,
+   <literal>&lt;</literal>,
+   <literal>&lt;=</literal>,
+   <literal>=</literal>,
+   <literal>&gt;=</literal> and
+   <literal>&gt;</literal>.
+   One might expect that <literal>&lt;&gt;</literal> should also be part of
+   the operator class, but it is not, because it would almost never be
+   useful to use a <literal>&lt;&gt;</literal> WHERE clause in an index
+   search.  (For some purposes, the planner treats <literal>&lt;&gt;</literal>
+   as associated with a btree operator class; but it finds that operator via
+   the <literal>=</literal> operator's negator link, rather than
+   from <structname>pg_amop</structname>.)
+  </para>
+
+  <para>
+   When several data types share near-identical sorting semantics, their
+   operator classes can be grouped into an operator family.  Doing so is
+   advantageous because it allows the planner to make deductions about
+   cross-type comparisons.  Each operator class within the family should
+   contain the single-type operators (and associated support functions)
+   for its input data type, while cross-type comparison operators and
+   support functions are <quote>loose</quote> in the family.  It is
+   recommendable that a complete set of cross-type operators be included
+   in the family, thus ensuring that the planner can represent any
+   comparison conditions that it deduces from transitivity.
+  </para>
+
+  <para>
+   There are some basic assumptions that a btree operator family must
+   satisfy:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     An <literal>=</literal> operator must be an equivalence relation; that
+     is, for all non-null values <replaceable>A</replaceable>,
+     <replaceable>B</replaceable>, <replaceable>C</replaceable> of the
+     data type:
+
+     <itemizedlist>
+      <listitem>
+       <para>
+        <replaceable>A</replaceable> <literal>=</literal>
+        <replaceable>A</replaceable> is true
+        (<firstterm>reflexive law</firstterm>)
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+        if <replaceable>A</replaceable> <literal>=</literal>
+        <replaceable>B</replaceable>,
+        then <replaceable>B</replaceable> <literal>=</literal>
+        <replaceable>A</replaceable>
+        (<firstterm>symmetric law</firstterm>)
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+        if <replaceable>A</replaceable> <literal>=</literal>
+        <replaceable>B</replaceable> and <replaceable>B</replaceable>
+        <literal>=</literal> <replaceable>C</replaceable>,
+        then <replaceable>A</replaceable> <literal>=</literal>
+        <replaceable>C</replaceable>
+        (<firstterm>transitive law</firstterm>)
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+     A <literal>&lt;</literal> operator must be a strong ordering relation;
+     that is, for all non-null values <replaceable>A</replaceable>,
+     <replaceable>B</replaceable>, <replaceable>C</replaceable>:
+
+     <itemizedlist>
+      <listitem>
+       <para>
+        <replaceable>A</replaceable> <literal>&lt;</literal>
+        <replaceable>A</replaceable> is false
+        (<firstterm>irreflexive law</firstterm>)
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+        if <replaceable>A</replaceable> <literal>&lt;</literal>
+        <replaceable>B</replaceable>
+        and <replaceable>B</replaceable> <literal>&lt;</literal>
+        <replaceable>C</replaceable>,
+        then <replaceable>A</replaceable> <literal>&lt;</literal>
+        <replaceable>C</replaceable>
+        (<firstterm>transitive law</firstterm>)
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </listitem>
+
+   <listitem>
+    <para>
+     Furthermore, the ordering is total; that is, for all non-null
+     values <replaceable>A</replaceable>, <replaceable>B</replaceable>:
+
+     <itemizedlist>
+      <listitem>
+       <para>
+        exactly one of <replaceable>A</replaceable> <literal>&lt;</literal>
+        <replaceable>B</replaceable>, <replaceable>A</replaceable>
+        <literal>=</literal> <replaceable>B</replaceable>, and
+        <replaceable>B</replaceable> <literal>&lt;</literal>
+        <replaceable>A</replaceable> is true
+        (<firstterm>trichotomy law</firstterm>)
+       </para>
+      </listitem>
+     </itemizedlist>
+
+     (The trichotomy law justifies the definition of the comparison support
+     function, of course.)
+    </para>
+   </listitem>
+  </itemizedlist>
+
+  <para>
+   The other three operators are defined in terms of <literal>=</literal>
+   and <literal>&lt;</literal> in the obvious way, and must act consistently
+   with them.
+  </para>
+
+  <para>
+   For an operator family supporting multiple data types, the above laws must
+   hold when <replaceable>A</replaceable>, <replaceable>B</replaceable>,
+   <replaceable>C</replaceable> are taken from any data types in the family.
+   The transitive laws are the trickiest to ensure, as in cross-type
+   situations they represent statements that the behaviors of two or three
+   different operators are consistent.
+   As an example, it would not work to put <type>float8</type>
+   and <type>numeric</type> into the same operator family, at least not with
+   the current semantics that <type>numeric</type> values are converted
+   to <type>float8</type> for comparison to a <type>float8</type>.  Because
+   of the limited accuracy of <type>float8</type>, this means there are
+   distinct <type>numeric</type> values that will compare equal to the
+   same <type>float8</type> value, and thus the transitive law would fail.
+  </para>
+
+  <para>
+   Another requirement for a multiple-data-type family is that any implicit
+   or binary-coercion casts that are defined between data types included in
+   the operator family must not change the associated sort ordering.
+  </para>
+
+  <para>
+   It should be fairly clear why a btree index requires these laws to hold
+   within a single data type: without them there is no ordering to arrange
+   the keys with.  Also, index searches using a comparison key of a
+   different data type require comparisons to behave sanely across two
+   data types.  The extensions to three or more data types within a family
+   are not strictly required by the btree index mechanism itself, but the
+   planner relies on them for optimization purposes.
+  </para>
+
+ </sect2>
+
+ <sect2 id="btree-support-funcs">
+  <title>B-Tree Support Functions</title>
+
+  <para>
+   As shown in <xref linkend="xindex-btree-support-table"/>, btree defines
+   one required and four optional support functions.  The five
+   user-defined methods are:
+  </para>
+  <variablelist>
+   <varlistentry>
+    <term><function>order</function></term>
+    <listitem>
+     <para>
+      For each combination of data types that a btree operator family
+      provides comparison operators for, it must provide a comparison
+      support function, registered in
+      <structname>pg_amproc</structname> with support function number 1
+      and
+      <structfield>amproclefttype</structfield>/<structfield>amprocrighttype</structfield>
+      equal to the left and right data types for the comparison (i.e.,
+      the same data types that the matching operators are registered
+      with in <structname>pg_amop</structname>).  The comparison
+      function must take two non-null values
+      <replaceable>A</replaceable> and <replaceable>B</replaceable> and
+      return an <type>int32</type> value that is
+      <literal>&lt;</literal> <literal>0</literal>,
+      <literal>0</literal>, or <literal>&gt;</literal>
+      <literal>0</literal> when <replaceable>A</replaceable>
+      <literal>&lt;</literal> <replaceable>B</replaceable>,
+      <replaceable>A</replaceable> <literal>=</literal>
+      <replaceable>B</replaceable>, or <replaceable>A</replaceable>
+      <literal>&gt;</literal> <replaceable>B</replaceable>,
+      respectively.  A null result is disallowed: all values of the
+      data type must be comparable.  See
+      <filename>src/backend/access/nbtree/nbtcompare.c</filename> for
+      examples.
+     </para>
+
+     <para>
+      If the compared values are of a collatable data type, the
+      appropriate collation OID will be passed to the comparison
+      support function, using the standard
+      <function>PG_GET_COLLATION()</function> mechanism.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><function>sortsupport</function></term>
+    <listitem>
+     <para>
+      Optionally, a btree operator family may provide <firstterm>sort
+       support</firstterm> function(s), registered under support
+      function number 2.  These functions allow implementing
+      comparisons for sorting purposes in a more efficient way than
+      naively calling the comparison support function.  The APIs
+      involved in this are defined in
+      <filename>src/include/utils/sortsupport.h</filename>.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><function>in_range</function></term>
+    <listitem>
+     <indexterm>
+      <primary>in_range support functions</primary>
+     </indexterm>
+
+     <indexterm>
+      <primary>support functions</primary>
+      <secondary>in_range</secondary>
+     </indexterm>
+     <para>
+      Optionally, a btree operator family may provide
+      <firstterm>in_range</firstterm> support function(s), registered
+      under support function number 3.  These are not used during btree
+      index operations; rather, they extend the semantics of the
+      operator family so that it can support window clauses containing
+      the <literal>RANGE</literal> <replaceable>offset</replaceable>
+      <literal>PRECEDING</literal> and <literal>RANGE</literal>
+      <replaceable>offset</replaceable> <literal>FOLLOWING</literal>
+      frame bound types (see <xref
+       linkend="syntax-window-functions"/>).  Fundamentally, the extra
+      information provided is how to add or subtract an
+      <replaceable>offset</replaceable> value in a way that is
+      compatible with the family's data ordering.
+     </para>
+
+     <para>
+      An <function>in_range</function> function must have the signature
+ <synopsis>
+ in_range(<replaceable>val</replaceable> type1, <replaceable>base</replaceable> type1, <replaceable>offset</replaceable> type2, <replaceable>sub</replaceable> bool, <replaceable>less</replaceable> bool)
+ returns bool
+ </synopsis>
+      <replaceable>val</replaceable> and
+      <replaceable>base</replaceable> must be of the same type, which
+      is one of the types supported by the operator family (i.e., a
+      type for which it provides an ordering).  However,
+      <replaceable>offset</replaceable> could be of a different type,
+      which might be one otherwise unsupported by the family.  An
+      example is that the built-in <literal>time_ops</literal> family
+      provides an <function>in_range</function> function that has
+      <replaceable>offset</replaceable> of type <type>interval</type>.
+      A family can provide <function>in_range</function> functions for
+      any of its supported types and one or more
+      <replaceable>offset</replaceable> types.  Each
+      <function>in_range</function> function should be entered in
+      <structname>pg_amproc</structname> with
+      <structfield>amproclefttype</structfield> equal to
+      <type>type1</type> and <structfield>amprocrighttype</structfield>
+      equal to <type>type2</type>.
+     </para>
+
+     <para>
+      The essential semantics of an <function>in_range</function>
+      function depend on the two Boolean flag parameters.  It should
+      add or subtract <replaceable>base</replaceable> and
+      <replaceable>offset</replaceable>, then compare
+      <replaceable>val</replaceable> to the result, as follows:
+      <itemizedlist>
+       <listitem>
+        <para>
+         if <literal>!</literal><replaceable>sub</replaceable> and
+         <literal>!</literal><replaceable>less</replaceable>, return
+         <replaceable>val</replaceable> <literal>&gt;=</literal>
+         (<replaceable>base</replaceable> <literal>+</literal>
+         <replaceable>offset</replaceable>)
+        </para>
+       </listitem>
+       <listitem>
+        <para>
+         if <literal>!</literal><replaceable>sub</replaceable> and
+         <replaceable>less</replaceable>, return
+         <replaceable>val</replaceable> <literal>&lt;=</literal>
+         (<replaceable>base</replaceable> <literal>+</literal>
+         <replaceable>offset</replaceable>)
+        </para>
+       </listitem>
+       <listitem>
+        <para>
+         if <replaceable>sub</replaceable> and
+         <literal>!</literal><replaceable>less</replaceable>, return
+         <replaceable>val</replaceable> <literal>&gt;=</literal>
+         (<replaceable>base</replaceable> <literal>-</literal>
+         <replaceable>offset</replaceable>)
+        </para>
+       </listitem>
+       <listitem>
+        <para>
+         if <replaceable>sub</replaceable> and
+         <replaceable>less</replaceable>, return
+         <replaceable>val</replaceable> <literal>&lt;=</literal>
+         (<replaceable>base</replaceable> <literal>-</literal>
+         <replaceable>offset</replaceable>)
+        </para>
+       </listitem>
+      </itemizedlist>
+      Before doing so, the function should check the sign of
+      <replaceable>offset</replaceable>: if it is less than zero, raise
+      error
+      <literal>ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE</literal>
+      (22013) with error text like <quote>invalid preceding or
+       following size in window function</quote>.  (This is required by
+      the SQL standard, although nonstandard operator families might
+      perhaps choose to ignore this restriction, since there seems to
+      be little semantic necessity for it.) This requirement is
+      delegated to the <function>in_range</function> function so that
+      the core code needn't understand what <quote>less than
+       zero</quote> means for a particular data type.
+     </para>
+
+     <para>
+      An additional expectation is that <function>in_range</function>
+      functions should, if practical, avoid throwing an error if
+      <replaceable>base</replaceable> <literal>+</literal>
+      <replaceable>offset</replaceable> or
+      <replaceable>base</replaceable> <literal>-</literal>
+      <replaceable>offset</replaceable> would overflow.  The correct
+      comparison result can be determined even if that value would be
+      out of the data type's range.  Note that if the data type
+      includes concepts such as <quote>infinity</quote> or
+      <quote>NaN</quote>, extra care may be needed to ensure that
+      <function>in_range</function>'s results agree with the normal
+      sort order of the operator family.
+     </para>
+
+     <para>
+      The results of the <function>in_range</function> function must be
+      consistent with the sort ordering imposed by the operator family.
+      To be precise, given any fixed values of
+      <replaceable>offset</replaceable> and
+      <replaceable>sub</replaceable>, then:
+      <itemizedlist>
+       <listitem>
+        <para>
+         If <function>in_range</function> with
+         <replaceable>less</replaceable> = true is true for some
+         <replaceable>val1</replaceable> and
+         <replaceable>base</replaceable>, it must be true for every
+         <replaceable>val2</replaceable> <literal>&lt;=</literal>
+         <replaceable>val1</replaceable> with the same
+         <replaceable>base</replaceable>.
+        </para>
+       </listitem>
+       <listitem>
+        <para>
+         If <function>in_range</function> with
+         <replaceable>less</replaceable> = true is false for some
+         <replaceable>val1</replaceable> and
+         <replaceable>base</replaceable>, it must be false for every
+         <replaceable>val2</replaceable> <literal>&gt;=</literal>
+         <replaceable>val1</replaceable> with the same
+         <replaceable>base</replaceable>.
+        </para>
+       </listitem>
+       <listitem>
+        <para>
+         If <function>in_range</function> with
+         <replaceable>less</replaceable> = true is true for some
+         <replaceable>val</replaceable> and
+         <replaceable>base1</replaceable>, it must be true for every
+         <replaceable>base2</replaceable> <literal>&gt;=</literal>
+         <replaceable>base1</replaceable> with the same
+         <replaceable>val</replaceable>.
+        </para>
+       </listitem>
+       <listitem>
+        <para>
+         If <function>in_range</function> with
+         <replaceable>less</replaceable> = true is false for some
+         <replaceable>val</replaceable> and
+         <replaceable>base1</replaceable>, it must be false for every
+         <replaceable>base2</replaceable> <literal>&lt;=</literal>
+         <replaceable>base1</replaceable> with the same
+         <replaceable>val</replaceable>.
+        </para>
+       </listitem>
+      </itemizedlist>
+      Analogous statements with inverted conditions hold when
+      <replaceable>less</replaceable> = false.
+     </para>
+
+     <para>
+      If the type being ordered (<type>type1</type>) is collatable, the
+      appropriate collation OID will be passed to the
+      <function>in_range</function> function, using the standard
+      PG_GET_COLLATION() mechanism.
+     </para>
+
+     <para>
+      <function>in_range</function> functions need not handle NULL
+      inputs, and typically will be marked strict.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><function>equalimage</function></term>
+    <listitem>
+     <para>
+      Optionally, a btree operator family may provide
+      <function>equalimage</function> (<quote>equality implies image
+       equality</quote>) support functions, registered under support
+      function number 4.  These functions allow the core code to
+      determine when it is safe to apply the btree deduplication
+      optimization.  Currently, <function>equalimage</function>
+      functions are only called when building or rebuilding an index.
+     </para>
+     <para>
+      An <function>equalimage</function> function must have the
+      signature
+ <synopsis>
+ equalimage(<replaceable>opcintype</replaceable> <type>oid</type>) returns bool
+ </synopsis>
+      The return value is static information about an operator class
+      and collation.  Returning <literal>true</literal> indicates that
+      the <function>order</function> function for the operator class is
+      guaranteed to only return <literal>0</literal> (<quote>arguments
+       are equal</quote>) when its <replaceable>A</replaceable> and
+      <replaceable>B</replaceable> arguments are also interchangeable
+      without any loss of semantic information.  Not registering an
+      <function>equalimage</function> function or returning
+      <literal>false</literal> indicates that this condition cannot be
+      assumed to hold.
+     </para>
+     <para>
+      The <replaceable>opcintype</replaceable> argument is the
+      <literal><structname>pg_type</structname>.oid</literal> of the
+      data type that the operator class indexes.  This is a convenience
+      that allows reuse of the same underlying
+      <function>equalimage</function> function across operator classes.
+      If <replaceable>opcintype</replaceable> is a collatable data
+      type, the appropriate collation OID will be passed to the
+      <function>equalimage</function> function, using the standard
+      <function>PG_GET_COLLATION()</function> mechanism.
+     </para>
+     <para>
+      As far as the operator class is concerned, returning
+      <literal>true</literal> indicates that deduplication is safe (or
+      safe for the collation whose OID was passed to its
+      <function>equalimage</function> function).  However, the core
+      code will only deem deduplication safe for an index when
+      <emphasis>every</emphasis> indexed column uses an operator class
+      that registers an <function>equalimage</function> function, and
+      each function actually returns <literal>true</literal> when
+      called.
+     </para>
+     <para>
+      Image equality is <emphasis>almost</emphasis> the same condition
+      as simple bitwise equality.  There is one subtle difference: When
+      indexing a varlena data type, the on-disk representation of two
+      image equal datums may not be bitwise equal due to inconsistent
+      application of <acronym>TOAST</acronym> compression on input.
+      Formally, when an operator class's
+      <function>equalimage</function> function returns
+      <literal>true</literal>, it is safe to assume that the
+      <literal>datum_image_eq()</literal> C function will always agree
+      with the operator class's <function>order</function> function
+      (provided that the same collation OID is passed to both the
+      <function>equalimage</function> and <function>order</function>
+      functions).
+     </para>
+     <para>
+      The core code is fundamentally unable to deduce anything about
+      the <quote>equality implies image equality</quote> status of an
+      operator class within a multiple-data-type family based on
+      details from other operator classes in the same family.  Also, it
+      is not sensible for an operator family to register a cross-type
+      <function>equalimage</function> function, and attempting to do so
+      will result in an error.  This is because <quote>equality implies
+       image equality</quote> status does not just depend on
+      sorting/equality semantics, which are more or less defined at the
+      operator family level.  In general, the semantics that one
+      particular data type implements must be considered separately.
+     </para>
+     <para>
+      The convention followed by the operator classes included with the
+      core <productname>PostgreSQL</productname> distribution is to
+      register a stock, generic <function>equalimage</function>
+      function.  Most operator classes register
+      <function>btequalimage()</function>, which indicates that
+      deduplication is safe unconditionally.  Operator classes for
+      collatable data types such as <type>text</type> register
+      <function>btvarstrequalimage()</function>, which indicates that
+      deduplication is safe with deterministic collations.  Best
+      practice for third-party extensions is to register their own
+      custom function to retain control.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><function>options</function></term>
+    <listitem>
+     <para>
+      Optionally, a B-tree operator family may provide
+      <function>options</function> (<quote>operator class specific
+      options</quote>) support functions, registered under support
+      function number 5.  These functions define a set of user-visible
+      parameters that control operator class behavior.
+     </para>
+     <para>
+      An <function>options</function> support function must have the
+      signature
+ <synopsis>
+ options(<replaceable>relopts</replaceable> <type>local_relopts *</type>) returns void
+ </synopsis>
+      The function is passed a pointer to a <structname>local_relopts</structname>
+      struct, which needs to be filled with a set of operator class
+      specific options.  The options can be accessed from other support
+      functions using the <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
+      <literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
+     </para>
+     <para>
+      Currently, no B-Tree operator class has an <function>options</function>
+      support function.  B-tree doesn't allow flexible representation of keys
+      like GiST, SP-GiST, GIN and BRIN do.  So, <function>options</function>
+      probably doesn't have much application in the current B-tree index
+      access method.  Nevertheless, this support function was added to B-tree
+      for uniformity, and will probably find uses during further
+      evolution of B-tree in <productname>PostgreSQL</productname>.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
+ </sect2>
+
+ <sect2 id="btree-implementation">
+  <title>Implementation</title>
+
+  <para>
+   This section covers B-Tree index implementation details that may be
+   of use to advanced users.  See
+   <filename>src/backend/access/nbtree/README</filename> in the source
+   distribution for a much more detailed, internals-focused description
+   of the B-Tree implementation.
+  </para>
+  <sect3 id="btree-structure">
+   <title>B-Tree Structure</title>
+   <para>
+    <productname>PostgreSQL</productname> B-Tree indexes are
+    multi-level tree structures, where each level of the tree can be
+    used as a doubly-linked list of pages.  A single metapage is stored
+    in a fixed position at the start of the first segment file of the
+    index.  All other pages are either leaf pages or internal pages.
+    Leaf pages are the pages on the lowest level of the tree.  All
+    other levels consist of internal pages.  Each leaf page contains
+    tuples that point to table rows.  Each internal page contains
+    tuples that point to the next level down in the tree.  Typically,
+    over 99% of all pages are leaf pages.  Both internal pages and leaf
+    pages use the standard page format described in <xref
+     linkend="storage-page-layout"/>.
+   </para>
+   <para>
+    New leaf pages are added to a B-Tree index when an existing leaf
+    page cannot fit an incoming tuple.  A <firstterm>page
+     split</firstterm> operation makes room for items that originally
+    belonged on the overflowing page by moving a portion of the items
+    to a new page.  Page splits must also insert a new
+    <firstterm>downlink</firstterm> to the new page in the parent page,
+    which may cause the parent to split in turn.  Page splits
+    <quote>cascade upwards</quote> in a recursive fashion.  When the
+    root page finally cannot fit a new downlink, a <firstterm>root page
+     split</firstterm> operation takes place.  This adds a new level to
+    the tree structure by creating a new root page that is one level
+    above the original root page.
+   </para>
+  </sect3>
+
+  <sect3 id="btree-deletion">
+   <title>Bottom-up Index Deletion</title>
+   <para>
+    B-Tree indexes are not directly aware that under MVCC, there might
+    be multiple extant versions of the same logical table row; to an
+    index, each tuple is an independent object that needs its own index
+    entry.  <quote>Version churn</quote> tuples may sometimes
+    accumulate and adversely affect query latency and throughput.  This
+    typically occurs with <command>UPDATE</command>-heavy workloads
+    where most individual updates cannot apply the
+    <link linkend="storage-hot"><acronym>HOT</acronym> optimization.</link>
+    Changing the value of only
+    one column covered by one index during an <command>UPDATE</command>
+    <emphasis>always</emphasis> necessitates a new set of index tuples
+    &mdash; one for <emphasis>each and every</emphasis> index on the
+    table.  Note in particular that this includes indexes that were not
+    <quote>logically modified</quote> by the <command>UPDATE</command>.
+    All indexes will need a successor physical index tuple that points
+    to the latest version in the table.  Each new tuple within each
+    index will generally need to coexist with the original
+    <quote>updated</quote> tuple for a short period of time (typically
+    until shortly after the <command>UPDATE</command> transaction
+    commits).
+   </para>
+   <para>
+    B-Tree indexes incrementally delete version churn index tuples by
+    performing <firstterm>bottom-up index deletion</firstterm> passes.
+    Each deletion pass is triggered in reaction to an anticipated
+    <quote>version churn page split</quote>.  This only happens with
+    indexes that are not logically modified by
+    <command>UPDATE</command> statements, where concentrated build up
+    of obsolete versions in particular pages would occur otherwise.  A
+    page split will usually be avoided, though it's possible that
+    certain implementation-level heuristics will fail to identify and
+    delete even one garbage index tuple (in which case a page split or
+    deduplication pass resolves the issue of an incoming new tuple not
+    fitting on a leaf page).  The worst-case number of versions that
+    any index scan must traverse (for any single logical row) is an
+    important contributor to overall system responsiveness and
+    throughput.  A bottom-up index deletion pass targets suspected
+    garbage tuples in a single leaf page based on
+    <emphasis>qualitative</emphasis> distinctions involving logical
+    rows and versions.  This contrasts with the <quote>top-down</quote>
+    index cleanup performed by autovacuum workers, which is triggered
+    when certain <emphasis>quantitative</emphasis> table-level
+    thresholds are exceeded (see <xref linkend="autovacuum"/>).
+   </para>
+   <note>
+    <para>
+     Not all deletion operations that are performed within B-Tree
+     indexes are bottom-up deletion operations.  There is a distinct
+     category of index tuple deletion: <firstterm>simple index tuple
+      deletion</firstterm>.  This is a deferred maintenance operation
+     that deletes index tuples that are known to be safe to delete
+     (those whose item identifier's <literal>LP_DEAD</literal> bit is
+     already set).  Like bottom-up index deletion, simple index
+     deletion takes place at the point that a page split is anticipated
+     as a way of avoiding the split.
+    </para>
+    <para>
+     Simple deletion is opportunistic in the sense that it can only
+     take place when recent index scans set the
+     <literal>LP_DEAD</literal> bits of affected items in passing.
+     Prior to <productname>PostgreSQL</productname> 14, the only
+     category of B-Tree deletion was simple deletion.  The main
+     differences between it and bottom-up deletion are that only the
+     former is opportunistically driven by the activity of passing
+     index scans, while only the latter specifically targets version
+     churn from <command>UPDATE</command>s that do not logically modify
+     indexed columns.
+    </para>
+   </note>
+   <para>
+    Bottom-up index deletion performs the vast majority of all garbage
+    index tuple cleanup for particular indexes with certain workloads.
+    This is expected with any B-Tree index that is subject to
+    significant version churn from <command>UPDATE</command>s that
+    rarely or never logically modify the columns that the index covers.
+    The average and worst-case number of versions per logical row can
+    be kept low purely through targeted incremental deletion passes.
+    It's quite possible that the on-disk size of certain indexes will
+    never increase by even one single page/block despite
+    <emphasis>constant</emphasis> version churn from
+    <command>UPDATE</command>s.  Even then, an exhaustive <quote>clean
+     sweep</quote> by a <command>VACUUM</command> operation (typically
+    run in an autovacuum worker process) will eventually be required as
+    a part of <emphasis>collective</emphasis> cleanup of the table and
+    each of its indexes.
+   </para>
+   <para>
+    Unlike <command>VACUUM</command>, bottom-up index deletion does not
+    provide any strong guarantees about how old the oldest garbage
+    index tuple may be.  No index can be permitted to retain
+    <quote>floating garbage</quote> index tuples that became dead prior
+    to a conservative cutoff point shared by the table and all of its
+    indexes collectively.  This fundamental table-level invariant makes
+    it safe to recycle table <acronym>TID</acronym>s.  This is how it
+    is possible for distinct logical rows to reuse the same table
+    <acronym>TID</acronym> over time (though this can never happen with
+    two logical rows whose lifetimes span the same
+    <command>VACUUM</command> cycle).
+   </para>
+  </sect3>
+
+  <sect3 id="btree-deduplication">
+   <title>Deduplication</title>
+   <para>
+    A duplicate is a leaf page tuple (a tuple that points to a table
+    row) where <emphasis>all</emphasis> indexed key columns have values
+    that match corresponding column values from at least one other leaf
+    page tuple in the same index.  Duplicate tuples are quite common in
+    practice.  B-Tree indexes can use a special, space-efficient
+    representation for duplicates when an optional technique is
+    enabled: <firstterm>deduplication</firstterm>.
+   </para>
+   <para>
+    Deduplication works by periodically merging groups of duplicate
+    tuples together, forming a single <firstterm>posting list</firstterm> tuple for each
+    group.  The column key value(s) only appear once in this
+    representation.  This is followed by a sorted array of
+    <acronym>TID</acronym>s that point to rows in the table.  This
+    significantly reduces the storage size of indexes where each value
+    (or each distinct combination of column values) appears several
+    times on average.  The latency of queries can be reduced
+    significantly.  Overall query throughput may increase
+    significantly.  The overhead of routine index vacuuming may also be
+    reduced significantly.
+   </para>
+   <note>
+    <para>
+     B-Tree deduplication is just as effective with
+     <quote>duplicates</quote> that contain a NULL value, even though
+     NULL values are never equal to each other according to the
+     <literal>=</literal> member of any B-Tree operator class.  As far
+     as any part of the implementation that understands the on-disk
+     B-Tree structure is concerned, NULL is just another value from the
+     domain of indexed values.
+    </para>
+   </note>
+   <para>
+    The deduplication process occurs lazily, when a new item is
+    inserted that cannot fit on an existing leaf page, though only when
+    index tuple deletion could not free sufficient space for the new
+    item (typically deletion is briefly considered and then skipped
+    over).  Unlike GIN posting list tuples, B-Tree posting list tuples
+    do not need to expand every time a new duplicate is inserted; they
+    are merely an alternative physical representation of the original
+    logical contents of the leaf page.  This design prioritizes
+    consistent performance with mixed read-write workloads.  Most
+    client applications will at least see a moderate performance
+    benefit from using deduplication.  Deduplication is enabled by
+    default.
+   </para>
+   <para>
+    <command>CREATE INDEX</command> and <command>REINDEX</command>
+    apply deduplication to create posting list tuples, though the
+    strategy they use is slightly different.  Each group of duplicate
+    ordinary tuples encountered in the sorted input taken from the
+    table is merged into a posting list tuple
+    <emphasis>before</emphasis> being added to the current pending leaf
+    page.  Individual posting list tuples are packed with as many
+    <acronym>TID</acronym>s as possible.  Leaf pages are written out in
+    the usual way, without any separate deduplication pass.  This
+    strategy is well-suited to <command>CREATE INDEX</command> and
+    <command>REINDEX</command> because they are once-off batch
+    operations.
+   </para>
+   <para>
+    Write-heavy workloads that don't benefit from deduplication due to
+    having few or no duplicate values in indexes will incur a small,
+    fixed performance penalty (unless deduplication is explicitly
+    disabled).  The <literal>deduplicate_items</literal> storage
+    parameter can be used to disable deduplication within individual
+    indexes.  There is never any performance penalty with read-only
+    workloads, since reading posting list tuples is at least as
+    efficient as reading the standard tuple representation.  Disabling
+    deduplication isn't usually helpful.
+   </para>
+   <para>
+    It is sometimes possible for unique indexes (as well as unique
+    constraints) to use deduplication.  This allows leaf pages to
+    temporarily <quote>absorb</quote> extra version churn duplicates.
+    Deduplication in unique indexes augments bottom-up index deletion,
+    especially in cases where a long-running transaction holds a
+    snapshot that blocks garbage collection.  The goal is to buy time
+    for the bottom-up index deletion strategy to become effective
+    again.  Delaying page splits until a single long-running
+    transaction naturally goes away can allow a bottom-up deletion pass
+    to succeed where an earlier deletion pass failed.
+   </para>
+   <tip>
+    <para>
+     A special heuristic is applied to determine whether a
+     deduplication pass in a unique index should take place.  It can
+     often skip straight to splitting a leaf page, avoiding a
+     performance penalty from wasting cycles on unhelpful deduplication
+     passes.  If you're concerned about the overhead of deduplication,
+     consider setting <literal>deduplicate_items = off</literal>
+     selectively.  Leaving deduplication enabled in unique indexes has
+     little downside.
+    </para>
+   </tip>
+   <para>
+    Deduplication cannot be used in all cases due to
+    implementation-level restrictions.  Deduplication safety is
+    determined when <command>CREATE INDEX</command> or
+    <command>REINDEX</command> is run.
+   </para>
+   <para>
+    Note that deduplication is deemed unsafe and cannot be used in the
+    following cases involving semantically significant differences
+    among equal datums:
+   </para>
+   <para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       <type>text</type>, <type>varchar</type>, and <type>char</type>
+       cannot use deduplication when a
+       <emphasis>nondeterministic</emphasis> collation is used.  Case
+       and accent differences must be preserved among equal datums.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       <type>numeric</type> cannot use deduplication.  Numeric display
+       scale must be preserved among equal datums.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       <type>jsonb</type> cannot use deduplication, since the
+       <type>jsonb</type> B-Tree operator class uses
+       <type>numeric</type> internally.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       <type>float4</type> and <type>float8</type> cannot use
+       deduplication.  These types have distinct representations for
+       <literal>-0</literal> and <literal>0</literal>, which are
+       nevertheless considered equal.  This difference must be
+       preserved.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+   <para>
+    There is one further implementation-level restriction that may be
+    lifted in a future version of
+    <productname>PostgreSQL</productname>:
+   </para>
+   <para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       Container types (such as composite types, arrays, or range
+       types) cannot use deduplication.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+   <para>
+    There is one further implementation-level restriction that applies
+    regardless of the operator class or collation used:
+   </para>
+   <para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       <literal>INCLUDE</literal> indexes can never use deduplication.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+
+  </sect3>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="gist">
+ <title>GiST Indexes</title>
+
+    <indexterm>
+     <primary>index</primary>
+     <secondary>GiST</secondary>
+    </indexterm>
+
+ <sect2 id="gist-intro">
+  <title>Introduction</title>
+
+  <para>
+    <acronym>GiST</acronym> stands for Generalized Search Tree.  It is a
+    balanced, tree-structured access method, that acts as a base template in
+    which to implement arbitrary indexing schemes. B-trees, R-trees and many
+    other indexing schemes can be implemented in <acronym>GiST</acronym>.
+  </para>
+
+  <para>
+   One advantage of <acronym>GiST</acronym> is that it allows the development
+   of custom data types with the appropriate access methods, by
+   an expert in the domain of the data type, rather than a database expert.
+  </para>
+
+   <para>
+     Some of the information here is derived from the University of California
+     at Berkeley's GiST Indexing Project
+     <ulink url="http://gist.cs.berkeley.edu/">web site</ulink> and
+     Marcel Kornacker's thesis,
+     <ulink url="http://www.sai.msu.su/~megera/postgres/gist/papers/concurrency/access-methods-for-next-generation.pdf.gz">
+     Access Methods for Next-Generation Database Systems</ulink>.
+     The <acronym>GiST</acronym>
+     implementation in <productname>PostgreSQL</productname> is primarily
+     maintained by Teodor Sigaev and Oleg Bartunov, and there is more
+     information on their
+     <ulink url="http://www.sai.msu.su/~megera/postgres/gist/">web site</ulink>.
+   </para>
+
+ </sect2>
+
+ <sect2 id="gist-builtin-opclasses">
+  <title>Built-in Operator Classes</title>
+
+  <para>
+   The core <productname>PostgreSQL</productname> distribution
+   includes the <acronym>GiST</acronym> operator classes shown in
+   <xref linkend="gist-builtin-opclasses-table"/>.
+   (Some of the optional modules described in <xref linkend="contrib"/>
+   provide additional <acronym>GiST</acronym> operator classes.)
+  </para>
+
+   <table id="gist-builtin-opclasses-table">
+    <title>Built-in <acronym>GiST</acronym> Operator Classes</title>
+    <tgroup cols="3">
+      <colspec colname="col1" colwidth="2*"/>
+      <colspec colname="col2" colwidth="3*"/>
+      <colspec colname="col3" colwidth="2*"/>
+     <thead>
+      <row>
+       <entry>Name</entry>
+       <entry>Indexable Operators</entry>
+       <entry>Ordering Operators</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry valign="middle" morerows="13"><literal>box_ops</literal></entry>
+       <entry><literal>&lt;&lt; (box, box)</literal></entry>
+       <entry valign="middle" morerows="13"><literal>&lt;-&gt; (box, point)</literal></entry>
+      </row>
+      <row><entry><literal>&amp;&lt; (box, box)</literal></entry></row>
+      <row><entry><literal>&amp;&amp; (box, box)</literal></entry></row>
+      <row><entry><literal>&amp;&gt; (box, box)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (box, box)</literal></entry></row>
+      <row><entry><literal>~= (box, box)</literal></entry></row>
+      <row><entry><literal>@&gt; (box, box)</literal></entry></row>
+      <row><entry><literal>&lt;@ (box, box)</literal></entry></row>
+      <row><entry><literal>&amp;&lt;| (box, box)</literal></entry></row>
+      <row><entry><literal>&lt;&lt;| (box, box)</literal></entry></row>
+      <row><entry><literal>|&gt;&gt; (box, box)</literal></entry></row>
+      <row><entry><literal>|&amp;&gt; (box, box)</literal></entry></row>
+      <row><entry><literal>~ (box, box)</literal></entry></row>
+      <row><entry><literal>@ (box, box)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="13"><literal>circle_ops</literal></entry>
+       <entry><literal>&lt;&lt; (circle, circle)</literal></entry>
+       <entry valign="middle" morerows="13"><literal>&lt;-&gt; (circle, point)</literal></entry>
+      </row>
+      <row><entry><literal>&amp;&lt; (circle, circle)</literal></entry></row>
+      <row><entry><literal>&amp;&gt; (circle, circle)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (circle, circle)</literal></entry></row>
+      <row><entry><literal>&lt;@ (circle, circle)</literal></entry></row>
+      <row><entry><literal>@&gt; (circle, circle)</literal></entry></row>
+      <row><entry><literal>~= (circle, circle)</literal></entry></row>
+      <row><entry><literal>&amp;&amp; (circle, circle)</literal></entry></row>
+      <row><entry><literal>|&gt;&gt; (circle, circle)</literal></entry></row>
+      <row><entry><literal>&lt;&lt;| (circle, circle)</literal></entry></row>
+      <row><entry><literal>&amp;&lt;| (circle, circle)</literal></entry></row>
+      <row><entry><literal>|&amp;&gt; (circle, circle)</literal></entry></row>
+      <row><entry><literal>@ (circle, circle)</literal></entry></row>
+      <row><entry><literal>~ (circle, circle)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="10"><literal>inet_ops</literal></entry>
+       <entry><literal>&lt;&lt; (inet, inet)</literal></entry>
+       <entry valign="middle" morerows="10"></entry>
+      </row>
+      <row><entry><literal>&lt;&lt;= (inet, inet)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (inet, inet)</literal></entry></row>
+      <row><entry><literal>&gt;&gt;= (inet, inet)</literal></entry></row>
+      <row><entry><literal>= (inet, inet)</literal></entry></row>
+      <row><entry><literal>&lt;&gt; (inet, inet)</literal></entry></row>
+      <row><entry><literal>&lt; (inet, inet)</literal></entry></row>
+      <row><entry><literal>&lt;= (inet, inet)</literal></entry></row>
+      <row><entry><literal>&gt; (inet, inet)</literal></entry></row>
+      <row><entry><literal>&gt;= (inet, inet)</literal></entry></row>
+      <row><entry><literal>&amp;&amp; (inet, inet)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="17"><literal>multirange_ops</literal></entry>
+       <entry><literal>= (anymultirange, anymultirange)</literal></entry>
+       <entry valign="middle" morerows="17"></entry>
+      </row>
+      <row><entry><literal>&amp;&amp; (anymultirange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&amp;&amp; (anymultirange, anyrange)</literal></entry></row>
+      <row><entry><literal>@&gt; (anymultirange, anyelement)</literal></entry></row>
+      <row><entry><literal>@&gt; (anymultirange, anymultirange)</literal></entry></row>
+      <row><entry><literal>@&gt; (anymultirange, anyrange)</literal></entry></row>
+      <row><entry><literal>&lt;@ (anymultirange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&lt;@ (anymultirange, anyrange)</literal></entry></row>
+      <row><entry><literal>&lt;&lt; (anymultirange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&lt;&lt; (anymultirange, anyrange)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (anymultirange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (anymultirange, anyrange)</literal></entry></row>
+      <row><entry><literal>&amp;&lt; (anymultirange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&amp;&lt; (anymultirange, anyrange)</literal></entry></row>
+      <row><entry><literal>&amp;&gt; (anymultirange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&amp;&gt; (anymultirange, anyrange)</literal></entry></row>
+      <row><entry><literal>-|- (anymultirange, anymultirange)</literal></entry></row>
+      <row><entry><literal>-|- (anymultirange, anyrange)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="7"><literal>point_ops</literal></entry>
+       <entry><literal>|&gt;&gt; (point, point)</literal></entry>
+       <entry valign="middle" morerows="7"><literal>&lt;-&gt; (point, point)</literal></entry>
+      </row>
+      <row><entry><literal>&lt;&lt; (point, point)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (point, point)</literal></entry></row>
+      <row><entry><literal>&lt;&lt;| (point, point)</literal></entry></row>
+      <row><entry><literal>~= (point, point)</literal></entry></row>
+      <row><entry><literal>&lt;@ (point, box)</literal></entry></row>
+      <row><entry><literal>&lt;@ (point, polygon)</literal></entry></row>
+      <row><entry><literal>&lt;@ (point, circle)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="13"><literal>poly_ops</literal></entry>
+       <entry><literal>&lt;&lt; (polygon, polygon)</literal></entry>
+       <entry valign="middle" morerows="13"><literal>&lt;-&gt; (polygon, point)</literal></entry>
+      </row>
+      <row><entry><literal>&amp;&lt; (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>&amp;&gt; (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>&lt;@ (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>@&gt; (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>~= (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>&amp;&amp; (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>&lt;&lt;| (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>&amp;&lt;| (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>|&amp;&gt; (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>|&gt;&gt; (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>@ (polygon, polygon)</literal></entry></row>
+      <row><entry><literal>~ (polygon, polygon)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="17"><literal>range_ops</literal></entry>
+       <entry><literal>= (anyrange, anyrange)</literal></entry>
+       <entry valign="middle" morerows="17"></entry>
+      </row>
+      <row><entry><literal>&amp;&amp; (anyrange, anyrange)</literal></entry></row>
+      <row><entry><literal>&amp;&amp; (anyrange, anymultirange)</literal></entry></row>
+      <row><entry><literal>@&gt; (anyrange, anyelement)</literal></entry></row>
+      <row><entry><literal>@&gt; (anyrange, anyrange)</literal></entry></row>
+      <row><entry><literal>@&gt; (anyrange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&lt;@ (anyrange, anyrange)</literal></entry></row>
+      <row><entry><literal>&lt;@ (anyrange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&lt;&lt; (anyrange, anyrange)</literal></entry></row>
+      <row><entry><literal>&lt;&lt; (anyrange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (anyrange, anyrange)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (anyrange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&amp;&lt; (anyrange, anyrange)</literal></entry></row>
+      <row><entry><literal>&amp;&lt; (anyrange, anymultirange)</literal></entry></row>
+      <row><entry><literal>&amp;&gt; (anyrange, anyrange)</literal></entry></row>
+      <row><entry><literal>&amp;&gt; (anyrange, anymultirange)</literal></entry></row>
+      <row><entry><literal>-|- (anyrange, anyrange)</literal></entry></row>
+      <row><entry><literal>-|- (anyrange, anymultirange)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="1"><literal>tsquery_ops</literal></entry>
+       <entry><literal>&lt;@ (tsquery, tsquery)</literal></entry>
+       <entry valign="middle" morerows="1"></entry>
+      </row>
+      <row><entry><literal>@&gt; (tsquery, tsquery)</literal></entry></row>
+      <row>
+       <entry valign="middle"><literal>tsvector_ops</literal></entry>
+       <entry><literal>@@ (tsvector, tsquery)</literal></entry>
+       <entry></entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   For historical reasons, the <literal>inet_ops</literal> operator class is
+   not the default class for types <type>inet</type> and <type>cidr</type>.
+   To use it, mention the class name in <command>CREATE INDEX</command>,
+   for example
+<programlisting>
+CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops);
+</programlisting>
+  </para>
+
+ </sect2>
+
+ <sect2 id="gist-extensibility">
+  <title>Extensibility</title>
+
+  <para>
+    Traditionally, implementing a new index access method meant a lot of
+    difficult work.  It was necessary to understand the inner workings of the
+    database, such as the lock manager and Write-Ahead Log.  The
+    <acronym>GiST</acronym> interface has a high level of abstraction,
+    requiring the access method implementer only to implement the semantics of
+    the data type being accessed.  The <acronym>GiST</acronym> layer itself
+    takes care of concurrency, logging and searching the tree structure.
+  </para>
+
+  <para>
+    This extensibility should not be confused with the extensibility of the
+    other standard search trees in terms of the data they can handle.  For
+    example, <productname>PostgreSQL</productname> supports extensible B-trees
+    and hash indexes. That means that you can use
+    <productname>PostgreSQL</productname> to build a B-tree or hash over any
+    data type you want. But B-trees only support range predicates
+    (<literal>&lt;</literal>, <literal>=</literal>, <literal>&gt;</literal>),
+    and hash indexes only support equality queries.
+  </para>
+
+  <para>
+    So if you index, say, an image collection with a
+    <productname>PostgreSQL</productname> B-tree, you can only issue queries
+    such as <quote>is imagex equal to imagey</quote>, <quote>is imagex less
+    than imagey</quote> and <quote>is imagex greater than imagey</quote>.
+    Depending on how you define <quote>equals</quote>, <quote>less than</quote>
+    and <quote>greater than</quote> in this context, this could be useful.
+    However, by using a <acronym>GiST</acronym> based index, you could create
+    ways to ask domain-specific questions, perhaps <quote>find all images of
+    horses</quote> or <quote>find all over-exposed images</quote>.
+  </para>
+
+  <para>
+    All it takes to get a <acronym>GiST</acronym> access method up and running
+    is to implement several user-defined methods, which define the behavior of
+    keys in the tree. Of course these methods have to be pretty fancy to
+    support fancy queries, but for all the standard queries (B-trees,
+    R-trees, etc.) they're relatively straightforward. In short,
+    <acronym>GiST</acronym> combines extensibility along with generality, code
+    reuse, and a clean interface.
+   </para>
+
+  <para>
+    There are five methods that an index operator class for
+    <acronym>GiST</acronym> must provide, and seven that are optional.
+    Correctness of the index is ensured
+    by proper implementation of the <function>same</function>, <function>consistent</function>
+    and <function>union</function> methods, while efficiency (size and speed) of the
+    index will depend on the <function>penalty</function> and <function>picksplit</function>
+    methods.
+    Two optional methods are <function>compress</function> and
+    <function>decompress</function>, which allow an index to have internal tree data of
+    a different type than the data it indexes. The leaves are to be of the
+    indexed data type, while the other tree nodes can be of any C struct (but
+    you still have to follow <productname>PostgreSQL</productname> data type rules here,
+    see about <literal>varlena</literal> for variable sized data). If the tree's
+    internal data type exists at the SQL level, the <literal>STORAGE</literal> option
+    of the <command>CREATE OPERATOR CLASS</command> command can be used.
+    The optional eighth method is <function>distance</function>, which is needed
+    if the operator class wishes to support ordered scans (nearest-neighbor
+    searches). The optional ninth method <function>fetch</function> is needed if the
+    operator class wishes to support index-only scans, except when the
+    <function>compress</function> method is omitted. The optional tenth method
+    <function>options</function> is needed if the operator class has
+    user-specified parameters.
+    The optional eleventh method <function>sortsupport</function> is used to
+    speed up building a <acronym>GiST</acronym> index.
+    The optional twelfth method <function>stratnum</function> is used to
+    translate well-known <literal>RT*StrategyNumber</literal>s (from
+    <filename>src/include/access/stratnum.h</filename>) into strategy numbers
+    used by the operator class.  This lets the core code look up operators for
+    temporal constraint indexes.
+  </para>
+
+  <variablelist>
+     <varlistentry>
+      <term><function>consistent</function></term>
+      <listitem>
+       <para>
+        Given an index entry <literal>p</literal> and a query value <literal>q</literal>,
+        this function determines whether the index entry is
+        <quote>consistent</quote> with the query; that is, could the predicate
+        <quote><replaceable>indexed_column</replaceable>
+        <replaceable>indexable_operator</replaceable> <literal>q</literal></quote> be true for
+        any row represented by the index entry?  For a leaf index entry this is
+        equivalent to testing the indexable condition, while for an internal
+        tree node this determines whether it is necessary to scan the subtree
+        of the index represented by the tree node.  When the result is
+        <literal>true</literal>, a <literal>recheck</literal> flag must also be returned.
+        This indicates whether the predicate is certainly true or only possibly
+        true.  If <literal>recheck</literal> = <literal>false</literal> then the index has
+        tested the predicate condition exactly, whereas if <literal>recheck</literal>
+        = <literal>true</literal> the row is only a candidate match.  In that case the
+        system will automatically evaluate the
+        <replaceable>indexable_operator</replaceable> against the actual row value to see
+        if it is really a match.  This convention allows
+        <acronym>GiST</acronym> to support both lossless and lossy index
+        structures.
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_consistent(internal, data_type, smallint, oid, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+
+         And the matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_consistent);
+
+Datum
+my_consistent(PG_FUNCTION_ARGS)
+{
+    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+    data_type  *query = PG_GETARG_DATA_TYPE_P(1);
+    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+    /* Oid subtype = PG_GETARG_OID(3); */
+    bool       *recheck = (bool *) PG_GETARG_POINTER(4);
+    data_type  *key = DatumGetDataType(entry-&gt;key);
+    bool        retval;
+
+    /*
+     * determine return value as a function of strategy, key and query.
+     *
+     * Use GIST_LEAF(entry) to know where you're called in the index tree,
+     * which comes handy when supporting the = operator for example (you could
+     * check for non empty union() in non-leaf nodes and equality in leaf
+     * nodes).
+     */
+
+    *recheck = true;        /* or false if check is exact */
+
+    PG_RETURN_BOOL(retval);
+}
+</programlisting>
+
+        Here, <varname>key</varname> is an element in the index and <varname>query</varname>
+        the value being looked up in the index. The <literal>StrategyNumber</literal>
+        parameter indicates which operator of your operator class is being
+        applied &mdash; it matches one of the operator numbers in the
+        <command>CREATE OPERATOR CLASS</command> command.
+       </para>
+
+       <para>
+        Depending on which operators you have included in the class, the data
+        type of <varname>query</varname> could vary with the operator, since it will
+        be whatever type is on the right-hand side of the operator, which might
+        be different from the indexed data type appearing on the left-hand side.
+        (The above code skeleton assumes that only one type is possible; if
+        not, fetching the <varname>query</varname> argument value would have to depend
+        on the operator.)  It is recommended that the SQL declaration of
+        the <function>consistent</function> function use the opclass's indexed data
+        type for the <varname>query</varname> argument, even though the actual type
+        might be something else depending on the operator.
+       </para>
+
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>union</function></term>
+      <listitem>
+       <para>
+        This method consolidates information in the tree.  Given a set of
+        entries, this function generates a new index entry that represents
+        all the given entries.
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_union(internal, internal)
+RETURNS storage_type
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+
+         And the matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_union);
+
+Datum
+my_union(PG_FUNCTION_ARGS)
+{
+    GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+    GISTENTRY  *ent = entryvec-&gt;vector;
+    data_type  *out,
+               *tmp,
+               *old;
+    int         numranges,
+                i = 0;
+
+    numranges = entryvec-&gt;n;
+    tmp = DatumGetDataType(ent[0].key);
+    out = tmp;
+
+    if (numranges == 1)
+    {
+        out = data_type_deep_copy(tmp);
+
+        PG_RETURN_DATA_TYPE_P(out);
+    }
+
+    for (i = 1; i &lt; numranges; i++)
+    {
+        old = out;
+        tmp = DatumGetDataType(ent[i].key);
+        out = my_union_implementation(out, tmp);
+    }
+
+    PG_RETURN_DATA_TYPE_P(out);
+}
+</programlisting>
+       </para>
+
+       <para>
+         As you can see, in this skeleton we're dealing with a data type
+         where <literal>union(X, Y, Z) = union(union(X, Y), Z)</literal>. It's easy
+         enough to support data types where this is not the case, by
+         implementing the proper union algorithm in this
+         <acronym>GiST</acronym> support method.
+       </para>
+
+       <para>
+         The result of the <function>union</function> function must be a value of the
+         index's storage type, whatever that is (it might or might not be
+         different from the indexed column's type).  The <function>union</function>
+         function should return a pointer to newly <function>palloc()</function>ed
+         memory. You can't just return the input value as-is, even if there is
+         no type change.
+       </para>
+
+       <para>
+        As shown above, the <function>union</function> function's
+        first <type>internal</type> argument is actually
+        a <structname>GistEntryVector</structname> pointer.  The second argument is a
+        pointer to an integer variable, which can be ignored.  (It used to be
+        required that the <function>union</function> function store the size of its
+        result value into that variable, but this is no longer necessary.)
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>compress</function></term>
+      <listitem>
+       <para>
+        Converts a data item into a format suitable for physical storage in
+        an index page.
+        If the <function>compress</function> method is omitted, data items are stored
+        in the index without modification.
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+
+         And the matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_compress);
+
+Datum
+my_compress(PG_FUNCTION_ARGS)
+{
+    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+    GISTENTRY  *retval;
+
+    if (entry-&gt;leafkey)
+    {
+        /* replace entry-&gt;key with a compressed version */
+        compressed_data_type *compressed_data = palloc(sizeof(compressed_data_type));
+
+        /* fill *compressed_data from entry-&gt;key ... */
+
+        retval = palloc(sizeof(GISTENTRY));
+        gistentryinit(*retval, PointerGetDatum(compressed_data),
+                      entry-&gt;rel, entry-&gt;page, entry-&gt;offset, FALSE);
+    }
+    else
+    {
+        /* typically we needn't do anything with non-leaf entries */
+        retval = entry;
+    }
+
+    PG_RETURN_POINTER(retval);
+}
+</programlisting>
+       </para>
+
+       <para>
+        You have to adapt <replaceable>compressed_data_type</replaceable> to the specific
+        type you're converting to in order to compress your leaf nodes, of
+        course.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>decompress</function></term>
+      <listitem>
+       <para>
+        Converts the stored representation of a data item into a format that
+        can be manipulated by the other GiST methods in the operator class.
+        If the <function>decompress</function> method is omitted, it is assumed that
+        the other GiST methods can work directly on the stored data format.
+        (<function>decompress</function> is not necessarily the reverse of
+        the <function>compress</function> method; in particular,
+        if <function>compress</function> is lossy then it's impossible
+        for <function>decompress</function> to exactly reconstruct the original
+        data.  <function>decompress</function> is not necessarily equivalent
+        to <function>fetch</function>, either, since the other GiST methods might not
+        require full reconstruction of the data.)
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+
+         And the matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_decompress);
+
+Datum
+my_decompress(PG_FUNCTION_ARGS)
+{
+    PG_RETURN_POINTER(PG_GETARG_POINTER(0));
+}
+</programlisting>
+
+         The above skeleton is suitable for the case where no decompression
+         is needed.  (But, of course, omitting the method altogether is even
+         easier, and is recommended in such cases.)
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>penalty</function></term>
+      <listitem>
+       <para>
+        Returns a value indicating the <quote>cost</quote> of inserting the new
+        entry into a particular branch of the tree.  Items will be inserted
+        down the path of least <function>penalty</function> in the tree.
+        Values returned by <function>penalty</function> should be non-negative.
+        If a negative value is returned, it will be treated as zero.
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_penalty(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;  -- in some cases penalty functions need not be strict
+</programlisting>
+
+         And the matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_penalty);
+
+Datum
+my_penalty(PG_FUNCTION_ARGS)
+{
+    GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
+    GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
+    float      *penalty = (float *) PG_GETARG_POINTER(2);
+    data_type  *orig = DatumGetDataType(origentry-&gt;key);
+    data_type  *new = DatumGetDataType(newentry-&gt;key);
+
+    *penalty = my_penalty_implementation(orig, new);
+    PG_RETURN_POINTER(penalty);
+}
+</programlisting>
+
+         For historical reasons, the <function>penalty</function> function doesn't
+         just return a <type>float</type> result; instead it has to store the value
+         at the location indicated by the third argument.  The return
+         value per se is ignored, though it's conventional to pass back the
+         address of that argument.
+       </para>
+
+       <para>
+         The <function>penalty</function> function is crucial to good performance of
+         the index. It'll get used at insertion time to determine which branch
+         to follow when choosing where to add the new entry in the tree. At
+         query time, the more balanced the index, the quicker the lookup.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>picksplit</function></term>
+      <listitem>
+       <para>
+        When an index page split is necessary, this function decides which
+        entries on the page are to stay on the old page, and which are to move
+        to the new page.
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+
+         And the matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_picksplit);
+
+Datum
+my_picksplit(PG_FUNCTION_ARGS)
+{
+    GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+    GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+    OffsetNumber maxoff = entryvec-&gt;n - 1;
+    GISTENTRY  *ent = entryvec-&gt;vector;
+    int         i,
+                nbytes;
+    OffsetNumber *left,
+               *right;
+    data_type  *tmp_union;
+    data_type  *unionL;
+    data_type  *unionR;
+    GISTENTRY **raw_entryvec;
+
+    maxoff = entryvec-&gt;n - 1;
+    nbytes = (maxoff + 1) * sizeof(OffsetNumber);
+
+    v-&gt;spl_left = (OffsetNumber *) palloc(nbytes);
+    left = v-&gt;spl_left;
+    v-&gt;spl_nleft = 0;
+
+    v-&gt;spl_right = (OffsetNumber *) palloc(nbytes);
+    right = v-&gt;spl_right;
+    v-&gt;spl_nright = 0;
+
+    unionL = NULL;
+    unionR = NULL;
+
+    /* Initialize the raw entry vector. */
+    raw_entryvec = (GISTENTRY **) malloc(entryvec-&gt;n * sizeof(void *));
+    for (i = FirstOffsetNumber; i &lt;= maxoff; i = OffsetNumberNext(i))
+        raw_entryvec[i] = &amp;(entryvec-&gt;vector[i]);
+
+    for (i = FirstOffsetNumber; i &lt;= maxoff; i = OffsetNumberNext(i))
+    {
+        int         real_index = raw_entryvec[i] - entryvec-&gt;vector;
+
+        tmp_union = DatumGetDataType(entryvec-&gt;vector[real_index].key);
+        Assert(tmp_union != NULL);
+
+        /*
+         * Choose where to put the index entries and update unionL and unionR
+         * accordingly. Append the entries to either v-&gt;spl_left or
+         * v-&gt;spl_right, and care about the counters.
+         */
+
+        if (my_choice_is_left(unionL, curl, unionR, curr))
+        {
+            if (unionL == NULL)
+                unionL = tmp_union;
+            else
+                unionL = my_union_implementation(unionL, tmp_union);
+
+            *left = real_index;
+            ++left;
+            ++(v-&gt;spl_nleft);
+        }
+        else
+        {
+            /*
+             * Same on the right
+             */
+        }
+    }
+
+    v-&gt;spl_ldatum = DataTypeGetDatum(unionL);
+    v-&gt;spl_rdatum = DataTypeGetDatum(unionR);
+    PG_RETURN_POINTER(v);
+}
+</programlisting>
+
+        Notice that the <function>picksplit</function> function's result is delivered
+        by modifying the passed-in <structname>v</structname> structure.  The return
+        value per se is ignored, though it's conventional to pass back the
+        address of <structname>v</structname>.
+       </para>
+
+       <para>
+         Like <function>penalty</function>, the <function>picksplit</function> function
+         is crucial to good performance of the index.  Designing suitable
+         <function>penalty</function> and <function>picksplit</function> implementations
+         is where the challenge of implementing well-performing
+         <acronym>GiST</acronym> indexes lies.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>same</function></term>
+      <listitem>
+       <para>
+        Returns true if two index entries are identical, false otherwise.
+        (An <quote>index entry</quote> is a value of the index's storage type,
+        not necessarily the original indexed column's type.)
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_same(storage_type, storage_type, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+
+         And the matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_same);
+
+Datum
+my_same(PG_FUNCTION_ARGS)
+{
+    prefix_range *v1 = PG_GETARG_PREFIX_RANGE_P(0);
+    prefix_range *v2 = PG_GETARG_PREFIX_RANGE_P(1);
+    bool       *result = (bool *) PG_GETARG_POINTER(2);
+
+    *result = my_eq(v1, v2);
+    PG_RETURN_POINTER(result);
+}
+</programlisting>
+
+         For historical reasons, the <function>same</function> function doesn't
+         just return a Boolean result; instead it has to store the flag
+         at the location indicated by the third argument.  The return
+         value per se is ignored, though it's conventional to pass back the
+         address of that argument.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>distance</function></term>
+      <listitem>
+       <para>
+        Given an index entry <literal>p</literal> and a query value <literal>q</literal>,
+        this function determines the index entry's
+        <quote>distance</quote> from the query value.  This function must be
+        supplied if the operator class contains any ordering operators.
+        A query using the ordering operator will be implemented by returning
+        index entries with the smallest <quote>distance</quote> values first,
+        so the results must be consistent with the operator's semantics.
+        For a leaf index entry the result just represents the distance to
+        the index entry; for an internal tree node, the result must be the
+        smallest distance that any child entry could have.
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+
+         And the matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_distance);
+
+Datum
+my_distance(PG_FUNCTION_ARGS)
+{
+    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+    data_type  *query = PG_GETARG_DATA_TYPE_P(1);
+    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+    /* Oid subtype = PG_GETARG_OID(3); */
+    /* bool *recheck = (bool *) PG_GETARG_POINTER(4); */
+    data_type  *key = DatumGetDataType(entry-&gt;key);
+    double      retval;
+
+    /*
+     * determine return value as a function of strategy, key and query.
+     */
+
+    PG_RETURN_FLOAT8(retval);
+}
+</programlisting>
+
+        The arguments to the <function>distance</function> function are identical to
+        the arguments of the <function>consistent</function> function.
+       </para>
+
+       <para>
+        Some approximation is allowed when determining the distance, so long
+        as the result is never greater than the entry's actual distance. Thus,
+        for example, distance to a bounding box is usually sufficient in
+        geometric applications.  For an internal tree node, the distance
+        returned must not be greater than the distance to any of the child
+        nodes. If the returned distance is not exact, the function must set
+        <literal>*recheck</literal> to true. (This is not necessary for internal tree
+        nodes; for them, the calculation is always assumed to be inexact.) In
+        this case the executor will calculate the accurate distance after
+        fetching the tuple from the heap, and reorder the tuples if necessary.
+       </para>
+
+       <para>
+        If the distance function returns <literal>*recheck = true</literal> for any
+        leaf node, the original ordering operator's return type must
+        be <type>float8</type> or <type>float4</type>, and the distance function's
+        result values must be comparable to those of the original ordering
+        operator, since the executor will sort using both distance function
+        results and recalculated ordering-operator results.  Otherwise, the
+        distance function's result values can be any finite <type>float8</type>
+        values, so long as the relative order of the result values matches the
+        order returned by the ordering operator.  (Infinity and minus infinity
+        are used internally to handle cases such as nulls, so it is not
+        recommended that <function>distance</function> functions return these values.)
+       </para>
+
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>fetch</function></term>
+      <listitem>
+       <para>
+        Converts the compressed index representation of a data item into the
+        original data type, for index-only scans. The returned data must be an
+        exact, non-lossy copy of the originally indexed value.
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+
+         The argument is a pointer to a <structname>GISTENTRY</structname> struct. On
+         entry, its <structfield>key</structfield> field contains a non-NULL leaf datum in
+         compressed form. The return value is another <structname>GISTENTRY</structname>
+         struct, whose <structfield>key</structfield> field contains the same datum in its
+         original, uncompressed form. If the opclass's compress function does
+         nothing for leaf entries, the <function>fetch</function> method can return the
+         argument as-is.  Or, if the opclass does not have a compress function,
+         the <function>fetch</function> method can be omitted as well, since it would
+         necessarily be a no-op.
+        </para>
+
+        <para>
+         The matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_fetch);
+
+Datum
+my_fetch(PG_FUNCTION_ARGS)
+{
+    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+    input_data_type *in = DatumGetPointer(entry->key);
+    fetched_data_type *fetched_data;
+    GISTENTRY  *retval;
+
+    retval = palloc(sizeof(GISTENTRY));
+    fetched_data = palloc(sizeof(fetched_data_type));
+
+    /*
+     * Convert 'fetched_data' into the a Datum of the original datatype.
+     */
+
+    /* fill *retval from fetched_data. */
+    gistentryinit(*retval, PointerGetDatum(converted_datum),
+                  entry->rel, entry->page, entry->offset, FALSE);
+
+    PG_RETURN_POINTER(retval);
+}
+</programlisting>
+       </para>
+
+       <para>
+        If the compress method is lossy for leaf entries, the operator class
+        cannot support index-only scans, and must not define
+        a <function>fetch</function> function.
+       </para>
+
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>options</function></term>
+      <listitem>
+       <para>
+        Allows definition of user-visible parameters that control operator
+        class behavior.
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_options(internal)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+       </para>
+
+       <para>
+        The function is passed a pointer to a <structname>local_relopts</structname>
+        struct, which needs to be filled with a set of operator class
+        specific options.  The options can be accessed from other support
+        functions using the <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
+        <literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
+       </para>
+
+        <para>
+         An example implementation of my_options() and parameters use
+         from other support functions are given below:
+
+<programlisting>
+typedef enum MyEnumType
+{
+    MY_ENUM_ON,
+    MY_ENUM_OFF,
+    MY_ENUM_AUTO
+} MyEnumType;
+
+typedef struct
+{
+    int32   vl_len_;    /* varlena header (do not touch directly!) */
+    int     int_param;  /* integer parameter */
+    double  real_param; /* real parameter */
+    MyEnumType enum_param; /* enum parameter */
+    int     str_param;  /* string parameter */
+} MyOptionsStruct;
+
+/* String representation of enum values */
+static relopt_enum_elt_def myEnumValues[] =
+{
+    {"on", MY_ENUM_ON},
+    {"off", MY_ENUM_OFF},
+    {"auto", MY_ENUM_AUTO},
+    {(const char *) NULL}   /* list terminator */
+};
+
+static char *str_param_default = "default";
+
+/*
+ * Sample validator: checks that string is not longer than 8 bytes.
+ */
+static void
+validate_my_string_relopt(const char *value)
+{
+    if (strlen(value) > 8)
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("str_param must be at most 8 bytes")));
+}
+
+/*
+ * Sample filler: switches characters to lower case.
+ */
+static Size
+fill_my_string_relopt(const char *value, void *ptr)
+{
+    char   *tmp = str_tolower(value, strlen(value), DEFAULT_COLLATION_OID);
+    int     len = strlen(tmp);
+
+    if (ptr)
+        strcpy((char *) ptr, tmp);
+
+    pfree(tmp);
+    return len + 1;
+}
+
+PG_FUNCTION_INFO_V1(my_options);
+
+Datum
+my_options(PG_FUNCTION_ARGS)
+{
+    local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+    init_local_reloptions(relopts, sizeof(MyOptionsStruct));
+    add_local_int_reloption(relopts, "int_param", "integer parameter",
+                            100, 0, 1000000,
+                            offsetof(MyOptionsStruct, int_param));
+    add_local_real_reloption(relopts, "real_param", "real parameter",
+                             1.0, 0.0, 1000000.0,
+                             offsetof(MyOptionsStruct, real_param));
+    add_local_enum_reloption(relopts, "enum_param", "enum parameter",
+                             myEnumValues, MY_ENUM_ON,
+                             "Valid values are: \"on\", \"off\" and \"auto\".",
+                             offsetof(MyOptionsStruct, enum_param));
+    add_local_string_reloption(relopts, "str_param", "string parameter",
+                               str_param_default,
+                               &amp;validate_my_string_relopt,
+                               &amp;fill_my_string_relopt,
+                               offsetof(MyOptionsStruct, str_param));
+
+    PG_RETURN_VOID();
+}
+
+PG_FUNCTION_INFO_V1(my_compress);
+
+Datum
+my_compress(PG_FUNCTION_ARGS)
+{
+    int     int_param = 100;
+    double  real_param = 1.0;
+    MyEnumType enum_param = MY_ENUM_ON;
+    char   *str_param = str_param_default;
+
+    /*
+     * Normally, when opclass contains 'options' method, then options are always
+     * passed to support functions.  However, if you add 'options' method to
+     * existing opclass, previously defined indexes have no options, so the
+     * check is required.
+     */
+    if (PG_HAS_OPCLASS_OPTIONS())
+    {
+        MyOptionsStruct *options = (MyOptionsStruct *) PG_GET_OPCLASS_OPTIONS();
+
+        int_param = options->int_param;
+        real_param = options->real_param;
+        enum_param = options->enum_param;
+        str_param = GET_STRING_RELOPTION(options, str_param);
+    }
+
+    /* the rest implementation of support function */
+}
+
+</programlisting>
+       </para>
+
+       <para>
+        Since the representation of the key in <acronym>GiST</acronym> is
+        flexible, it may depend on user-specified parameters.  For instance,
+        the length of key signature may be specified.  See
+        <literal>gtsvector_options()</literal> for example.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>sortsupport</function></term>
+      <listitem>
+       <para>
+        Returns a comparator function to sort data in a way that preserves
+        locality. It is used by <command>CREATE INDEX</command> and
+        <command>REINDEX</command> commands. The quality of the created index
+        depends on how well the sort order determined by the comparator function
+        preserves locality of the inputs.
+       </para>
+       <para>
+        The <function>sortsupport</function> method is optional. If it is not
+        provided, <command>CREATE INDEX</command> builds the index by inserting
+        each tuple to the tree using the <function>penalty</function> and
+        <function>picksplit</function> functions, which is much slower.
+       </para>
+
+       <para>
+        The <acronym>SQL</acronym> declaration of the function must look like
+        this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_sortsupport(internal)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+
+        The argument is a pointer to a <structname>SortSupport</structname>
+        struct. At a minimum, the function must fill in its comparator field.
+        The comparator takes three arguments: two Datums to compare, and
+        a pointer to the <structname>SortSupport</structname> struct. The
+        Datums are the two indexed values in the format that they are stored
+        in the index; that is, in the format returned by the
+        <function>compress</function> method. The full API is defined in
+        <filename>src/include/utils/sortsupport.h</filename>.
+        </para>
+
+        <para>
+         The matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_sortsupport);
+
+static int
+my_fastcmp(Datum x, Datum y, SortSupport ssup)
+{
+  /* establish order between x and y by computing some sorting value z */
+
+  int z1 = ComputeSpatialCode(x);
+  int z2 = ComputeSpatialCode(y);
+
+  return z1 == z2 ? 0 : z1 > z2 ? 1 : -1;
+}
+
+Datum
+my_sortsupport(PG_FUNCTION_ARGS)
+{
+  SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
+
+  ssup->comparator = my_fastcmp;
+  PG_RETURN_VOID();
+}
+</programlisting>
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>stratnum</function></term>
+      <listitem>
+       <para>
+        Given an <literal>RT*StrategyNumber</literal> value from
+        <filename>src/include/access/stratnum.h</filename>, returns a strategy
+        number used by this operator class for matching functionality.  The
+        function should return <literal>InvalidStrategy</literal> if the
+        operator class has no matching strategy.
+       </para>
+
+       <para>
+        This is used for temporal index constraints (i.e., <literal>PRIMARY
+        KEY</literal> and <literal>UNIQUE</literal>).  If the operator class
+        provides this function and it returns results for
+        <literal>RTEqualStrategyNumber</literal>, it can be used in the
+        non-<literal>WITHOUT OVERLAPS</literal> part(s) of an index constraint.
+        If it returns results for <literal>RTOverlapStrategyNumber</literal>,
+        the operator class can be used in the <literal>WITHOUT
+        OVERLAPS</literal> part of an index constraint.
+       </para>
+
+       <para>
+        The <acronym>SQL</acronym> declaration of the function must look like
+        this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_stratnum(integer)
+RETURNS integer
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+       </para>
+
+        <para>
+         The matching code in the C module could then follow this skeleton:
+
+<programlisting>
+PG_FUNCTION_INFO_V1(my_stratnum);
+
+Datum
+my_stratnum(PG_FUNCTION_ARGS)
+{
+    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(1);
+    StrategyNumber ret = InvalidStrategy;
+
+    switch (strategy)
+    {
+        case RTEqualStrategyNumber:
+            ret = BTEqualStrategyNumber;
+    }
+
+    PG_RETURN_UINT16(ret);
+}
+		</programlisting>
+			   </para>
+
+       <para>
+        One translation function is provided by
+        <productname>PostgreSQL</productname>:
+        <literal>gist_stratnum_identity</literal> is for operator classes that
+        already use the <literal>RT*StrategyNumber</literal> constants.  It
+        returns whatever is passed to it.  The <literal>btree_gist</literal>
+        extension defines a second translation function,
+        <literal>gist_stratnum_btree</literal>, for operator classes that use
+        the <literal>BT*StrategyNumber</literal> constants.
+       </para>
+      </listitem>
+     </varlistentry>
+   </variablelist>
+
+   <para>
+    All the GiST support methods are normally called in short-lived memory
+    contexts; that is, <varname>CurrentMemoryContext</varname> will get reset after
+    each tuple is processed.  It is therefore not very important to worry about
+    pfree'ing everything you palloc.  However, in some cases it's useful for a
+    support method to cache data across repeated calls.  To do that, allocate
+    the longer-lived data in <literal>fcinfo-&gt;flinfo-&gt;fn_mcxt</literal>, and
+    keep a pointer to it in <literal>fcinfo-&gt;flinfo-&gt;fn_extra</literal>.  Such
+    data will survive for the life of the index operation (e.g., a single GiST
+    index scan, index build, or index tuple insertion).  Be careful to pfree
+    the previous value when replacing a <literal>fn_extra</literal> value, or the leak
+    will accumulate for the duration of the operation.
+   </para>
+
+ </sect2>
+
+ <sect2 id="gist-implementation">
+  <title>Implementation</title>
+
+  <sect3 id="gist-buffering-build">
+   <title>GiST Index Build Methods</title>
+
+   <para>
+    The simplest way to build a GiST index is just to insert all the entries,
+    one by one.  This tends to be slow for large indexes, because if the
+    index tuples are scattered across the index and the index is large enough
+    to not fit in cache, a lot of random I/O will be
+    needed.  <productname>PostgreSQL</productname> supports two alternative
+    methods for initial build of a GiST index: <firstterm>sorted</firstterm>
+    and <firstterm>buffered</firstterm> modes.
+   </para>
+
+   <para>
+    The sorted method is only available if each of the opclasses used by the
+    index provides a <function>sortsupport</function> function, as described
+    in <xref linkend="gist-extensibility"/>.  If they do, this method is
+    usually the best, so it is used by default.
+   </para>
+
+   <para>
+    The buffered method works by not inserting tuples directly into the index
+    right away.  It can dramatically reduce the amount of random I/O needed
+    for non-ordered data sets.  For well-ordered data sets the benefit is
+    smaller or non-existent, because only a small number of pages receive new
+    tuples at a time, and those pages fit in cache even if the index as a
+    whole does not.
+   </para>
+
+   <para>
+    The buffered method needs to call the <function>penalty</function>
+    function more often than the simple method does, which consumes some
+    extra CPU resources. Also, the buffers need temporary disk space, up to
+    the size of the resulting index. Buffering can also influence the quality
+    of the resulting index, in both positive and negative directions. That
+    influence depends on various factors, like the distribution of the input
+    data and the operator class implementation.
+   </para>
+
+   <para>
+    If sorting is not possible, then by default a GiST index build switches
+    to the buffering method when the index size reaches
+    <xref linkend="guc-effective-cache-size"/>.  Buffering can be manually
+    forced or prevented by the <literal>buffering</literal> parameter to the
+    CREATE INDEX command.  The default behavior is good for most cases, but
+    turning buffering off might speed up the build somewhat if the input data
+    is ordered.
+   </para>
+
+  </sect3>
+ </sect2>
+
+ <sect2 id="gist-examples">
+  <title>Examples</title>
+
+  <para>
+   The <productname>PostgreSQL</productname> source distribution includes
+   several examples of index methods implemented using
+   <acronym>GiST</acronym>.  The core system currently provides text search
+   support (indexing for <type>tsvector</type> and <type>tsquery</type>) as well as
+   R-Tree equivalent functionality for some of the built-in geometric data types
+   (see <filename>src/backend/access/gist/gistproc.c</filename>).  The following
+   <filename>contrib</filename> modules also contain <acronym>GiST</acronym>
+   operator classes:
+
+  <variablelist>
+   <varlistentry>
+    <term><filename>btree_gist</filename></term>
+    <listitem>
+     <para>B-tree equivalent functionality for several data types</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><filename>cube</filename></term>
+    <listitem>
+     <para>Indexing for multidimensional cubes</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><filename>hstore</filename></term>
+    <listitem>
+     <para>Module for storing (key, value) pairs</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><filename>intarray</filename></term>
+    <listitem>
+     <para>RD-Tree for one-dimensional array of int4 values</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><filename>ltree</filename></term>
+    <listitem>
+     <para>Indexing for tree-like structures</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><filename>pg_trgm</filename></term>
+    <listitem>
+     <para>Text similarity using trigram matching</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><filename>seg</filename></term>
+    <listitem>
+     <para>Indexing for <quote>float ranges</quote></para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+  </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="spgist">
+ <title>SP-GiST Indexes</title>
+
+    <indexterm>
+     <primary>index</primary>
+     <secondary>SP-GiST</secondary>
+    </indexterm>
+
+ <sect2 id="spgist-intro">
+  <title>Introduction</title>
+
+  <para>
+   <acronym>SP-GiST</acronym> is an abbreviation for space-partitioned
+   <acronym>GiST</acronym>.  <acronym>SP-GiST</acronym> supports partitioned
+   search trees, which facilitate development of a wide range of different
+   non-balanced data structures, such as quad-trees, k-d trees, and radix
+   trees (tries).  The common feature of these structures is that they
+   repeatedly divide the search space into partitions that need not be
+   of equal size.  Searches that are well matched to the partitioning rule
+   can be very fast.
+  </para>
+
+  <para>
+   These popular data structures were originally developed for in-memory
+   usage.  In main memory, they are usually designed as a set of dynamically
+   allocated nodes linked by pointers.  This is not suitable for direct
+   storing on disk, since these chains of pointers can be rather long which
+   would require too many disk accesses.  In contrast, disk-based data
+   structures should have a high fanout to minimize I/O.  The challenge
+   addressed by <acronym>SP-GiST</acronym> is to map search tree nodes to
+   disk pages in such a way that a search need access only a few disk pages,
+   even if it traverses many nodes.
+  </para>
+
+  <para>
+   Like <acronym>GiST</acronym>, <acronym>SP-GiST</acronym> is meant to allow
+   the development of custom data types with the appropriate access methods,
+   by an expert in the domain of the data type, rather than a database expert.
+  </para>
+
+  <para>
+   Some of the information here is derived from Purdue University's
+   SP-GiST Indexing Project
+   <ulink url="https://www.cs.purdue.edu/spgist/">web site</ulink>.
+   The <acronym>SP-GiST</acronym> implementation in
+   <productname>PostgreSQL</productname> is primarily maintained by Teodor
+   Sigaev and Oleg Bartunov, and there is more information on their
+   <!-- URL will be changed -->
+   <ulink url="http://www.sai.msu.su/~megera/wiki/spgist_dev">web site</ulink>.
+  </para>
+
+ </sect2>
+
+ <sect2 id="spgist-builtin-opclasses">
+  <title>Built-in Operator Classes</title>
+
+  <para>
+   The core <productname>PostgreSQL</productname> distribution
+   includes the <acronym>SP-GiST</acronym> operator classes shown in
+   <xref linkend="spgist-builtin-opclasses-table"/>.
+  </para>
+
+   <table id="spgist-builtin-opclasses-table">
+    <title>Built-in <acronym>SP-GiST</acronym> Operator Classes</title>
+    <tgroup cols="3">
+     <thead>
+      <row>
+       <entry>Name</entry>
+       <entry>Indexable Operators</entry>
+       <entry>Ordering Operators</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry valign="middle" morerows="11"><literal>box_ops</literal></entry>
+       <entry><literal>&lt;&lt; (box,box)</literal></entry>
+       <entry valign="middle" morerows="11"><literal>&lt;-&gt; (box,point)</literal></entry>
+      </row>
+      <row><entry><literal>&amp;&lt; (box,box)</literal></entry></row>
+      <row><entry><literal>&amp;&gt; (box,box)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (box,box)</literal></entry></row>
+      <row><entry><literal>&lt;@ (box,box)</literal></entry></row>
+      <row><entry><literal>@&gt; (box,box)</literal></entry></row>
+      <row><entry><literal>~= (box,box)</literal></entry></row>
+      <row><entry><literal>&amp;&amp; (box,box)</literal></entry></row>
+      <row><entry><literal>&lt;&lt;| (box,box)</literal></entry></row>
+      <row><entry><literal>&amp;&lt;| (box,box)</literal></entry></row>
+      <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row>
+      <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="10"><literal>inet_ops</literal></entry>
+       <entry><literal>&lt;&lt; (inet,inet)</literal></entry>
+       <entry valign="middle" morerows="10"></entry>
+      </row>
+      <row><entry><literal>&lt;&lt;= (inet,inet)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (inet,inet)</literal></entry></row>
+      <row><entry><literal>&gt;&gt;= (inet,inet)</literal></entry></row>
+      <row><entry><literal>= (inet,inet)</literal></entry></row>
+      <row><entry><literal>&lt;&gt; (inet,inet)</literal></entry></row>
+      <row><entry><literal>&lt; (inet,inet)</literal></entry></row>
+      <row><entry><literal>&lt;= (inet,inet)</literal></entry></row>
+      <row><entry><literal>&gt; (inet,inet)</literal></entry></row>
+      <row><entry><literal>&gt;= (inet,inet)</literal></entry></row>
+      <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="5"><literal>kd_point_ops</literal></entry>
+       <entry><literal>|&gt;&gt; (point,point)</literal></entry>
+       <entry valign="middle" morerows="5"><literal>&lt;-&gt; (point,point)</literal></entry>
+      </row>
+      <row><entry><literal>&lt;&lt; (point,point)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (point,point)</literal></entry></row>
+      <row><entry><literal>&lt;&lt;| (point,point)</literal></entry></row>
+      <row><entry><literal>~= (point,point)</literal></entry></row>
+      <row><entry><literal>&lt;@ (point,box)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="11"><literal>poly_ops</literal></entry>
+       <entry><literal>&lt;&lt; (polygon,polygon)</literal></entry>
+       <entry valign="middle" morerows="11"><literal>&lt;-&gt; (polygon,point)</literal></entry>
+      </row>
+      <row><entry><literal>&amp;&lt; (polygon,polygon)</literal></entry></row>
+      <row><entry><literal>&amp;&gt; (polygon,polygon)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (polygon,polygon)</literal></entry></row>
+      <row><entry><literal>&lt;@ (polygon,polygon)</literal></entry></row>
+      <row><entry><literal>@&gt; (polygon,polygon)</literal></entry></row>
+      <row><entry><literal>~= (polygon,polygon)</literal></entry></row>
+      <row><entry><literal>&amp;&amp; (polygon,polygon)</literal></entry></row>
+      <row><entry><literal>&lt;&lt;| (polygon,polygon)</literal></entry></row>
+      <row><entry><literal>&amp;&lt;| (polygon,polygon)</literal></entry></row>
+      <row><entry><literal>|&gt;&gt; (polygon,polygon)</literal></entry></row>
+      <row><entry><literal>|&amp;&gt; (polygon,polygon)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="5"><literal>quad_point_ops</literal></entry>
+       <entry><literal>|&gt;&gt; (point,point)</literal></entry>
+       <entry valign="middle" morerows="5"><literal>&lt;-&gt; (point,point)</literal></entry>
+      </row>
+      <row><entry><literal>&lt;&lt; (point,point)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (point,point)</literal></entry></row>
+      <row><entry><literal>&lt;&lt;| (point,point)</literal></entry></row>
+      <row><entry><literal>~= (point,point)</literal></entry></row>
+      <row><entry><literal>&lt;@ (point,box)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="9"><literal>range_ops</literal></entry>
+       <entry><literal>= (anyrange,anyrange)</literal></entry>
+       <entry valign="middle" morerows="9"></entry>
+      </row>
+      <row><entry><literal>&amp;&amp; (anyrange,anyrange)</literal></entry></row>
+      <row><entry><literal>@&gt; (anyrange,anyelement)</literal></entry></row>
+      <row><entry><literal>@&gt; (anyrange,anyrange)</literal></entry></row>
+      <row><entry><literal>&lt;@ (anyrange,anyrange)</literal></entry></row>
+      <row><entry><literal>&lt;&lt; (anyrange,anyrange)</literal></entry></row>
+      <row><entry><literal>&gt;&gt; (anyrange,anyrange)</literal></entry></row>
+      <row><entry><literal>&amp;&lt; (anyrange,anyrange)</literal></entry></row>
+      <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row>
+      <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row>
+
+      <row>
+       <entry valign="middle" morerows="9"><literal>text_ops</literal></entry>
+       <entry><literal>= (text,text)</literal></entry>
+       <entry valign="middle" morerows="9"></entry>
+      </row>
+      <row><entry><literal>&lt; (text,text)</literal></entry></row>
+      <row><entry><literal>&lt;= (text,text)</literal></entry></row>
+      <row><entry><literal>&gt; (text,text)</literal></entry></row>
+      <row><entry><literal>&gt;= (text,text)</literal></entry></row>
+      <row><entry><literal>~&lt;~ (text,text)</literal></entry></row>
+      <row><entry><literal>~&lt;=~ (text,text)</literal></entry></row>
+      <row><entry><literal>~&gt;=~ (text,text)</literal></entry></row>
+      <row><entry><literal>~&gt;~ (text,text)</literal></entry></row>
+      <row><entry><literal>^@ (text,text)</literal></entry></row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   Of the two operator classes for type <type>point</type>,
+   <literal>quad_point_ops</literal> is the default.  <literal>kd_point_ops</literal>
+   supports the same operators but uses a different index data structure that
+   may offer better performance in some applications.
+  </para>
+  <para>
+   The <literal>quad_point_ops</literal>, <literal>kd_point_ops</literal> and
+   <literal>poly_ops</literal> operator classes support the <literal>&lt;-&gt;</literal>
+   ordering operator, which enables the k-nearest neighbor (<literal>k-NN</literal>)
+   search over indexed point or polygon data sets.
+  </para>
+
+ </sect2>
+
+ <sect2 id="spgist-extensibility">
+  <title>Extensibility</title>
+
+  <para>
+   <acronym>SP-GiST</acronym> offers an interface with a high level of
+   abstraction, requiring the access method developer to implement only
+   methods specific to a given data type. The <acronym>SP-GiST</acronym> core
+   is responsible for efficient disk mapping and searching the tree structure.
+   It also takes care of concurrency and logging considerations.
+  </para>
+
+  <para>
+   Leaf tuples of an <acronym>SP-GiST</acronym> tree usually contain values
+   of the same data type as the indexed column, although it is also possible
+   for them to contain lossy representations of the indexed column.
+   Leaf tuples stored at the root level will directly represent
+   the original indexed data value, but leaf tuples at lower
+   levels might contain only a partial value, such as a suffix.
+   In that case the operator class support functions must be able to
+   reconstruct the original value using information accumulated from the
+   inner tuples that are passed through to reach the leaf level.
+  </para>
+
+  <para>
+   When an <acronym>SP-GiST</acronym> index is created with
+   <literal>INCLUDE</literal> columns, the values of those columns are also
+   stored in leaf tuples.  The <literal>INCLUDE</literal> columns are of no
+   concern to the <acronym>SP-GiST</acronym> operator class, so they are
+   not discussed further here.
+  </para>
+
+  <para>
+   Inner tuples are more complex, since they are branching points in the
+   search tree.  Each inner tuple contains a set of one or more
+   <firstterm>nodes</firstterm>, which represent groups of similar leaf values.
+   A node contains a downlink that leads either to another, lower-level inner
+   tuple, or to a short list of leaf tuples that all lie on the same index page.
+   Each node normally has a <firstterm>label</firstterm> that describes it; for example,
+   in a radix tree the node label could be the next character of the string
+   value.  (Alternatively, an operator class can omit the node labels, if it
+   works with a fixed set of nodes for all inner tuples;
+   see <xref linkend="spgist-null-labels"/>.)
+   Optionally, an inner tuple can have a <firstterm>prefix</firstterm> value
+   that describes all its members.  In a radix tree this could be the common
+   prefix of the represented strings.  The prefix value is not necessarily
+   really a prefix, but can be any data needed by the operator class;
+   for example, in a quad-tree it can store the central point that the four
+   quadrants are measured with respect to.  A quad-tree inner tuple would
+   then also contain four nodes corresponding to the quadrants around this
+   central point.
+  </para>
+
+  <para>
+   Some tree algorithms require knowledge of level (or depth) of the current
+   tuple, so the <acronym>SP-GiST</acronym> core provides the possibility for
+   operator classes to manage level counting while descending the tree.
+   There is also support for incrementally reconstructing the represented
+   value when that is needed, and for passing down additional data (called
+   <firstterm>traverse values</firstterm>) during a tree descent.
+  </para>
+
+  <note>
+   <para>
+    The <acronym>SP-GiST</acronym> core code takes care of null entries.
+    Although <acronym>SP-GiST</acronym> indexes do store entries for nulls
+    in indexed columns, this is hidden from the index operator class code:
+    no null index entries or search conditions will ever be passed to the
+    operator class methods.  (It is assumed that <acronym>SP-GiST</acronym>
+    operators are strict and so cannot succeed for null values.)  Null values
+    are therefore not discussed further here.
+   </para>
+  </note>
+
+  <para>
+   There are five user-defined methods that an index operator class for
+   <acronym>SP-GiST</acronym> must provide, and two are optional.  All five
+   mandatory methods follow the convention of accepting two <type>internal</type>
+   arguments, the first of which is a pointer to a C struct containing input
+   values for the support method, while the second argument is a pointer to a
+   C struct where output values must be placed.  Four of the mandatory methods just
+   return <type>void</type>, since all their results appear in the output struct; but
+   <function>leaf_consistent</function> returns a <type>boolean</type> result.
+   The methods must not modify any fields of their input structs.  In all
+   cases, the output struct is initialized to zeroes before calling the
+   user-defined method.  The optional sixth method <function>compress</function>
+   accepts a <type>datum</type> to be indexed as the only argument and returns a value suitable
+   for physical storage in a leaf tuple.  The optional seventh method
+   <function>options</function> accepts an <type>internal</type> pointer to a C struct, where
+   opclass-specific parameters should be placed, and returns <type>void</type>.
+  </para>
+
+  <para>
+   The five mandatory user-defined methods are:
+  </para>
+
+  <variablelist>
+     <varlistentry>
+      <term><function>config</function></term>
+      <listitem>
+       <para>
+        Returns static information about the index implementation, including
+        the data type OIDs of the prefix and node label data types.
+       </para>
+      <para>
+       The <acronym>SQL</acronym> declaration of the function must look like this:
+<programlisting>
+CREATE FUNCTION my_config(internal, internal) RETURNS void ...
+</programlisting>
+       The first argument is a pointer to a <structname>spgConfigIn</structname>
+       C struct, containing input data for the function.
+       The second argument is a pointer to a <structname>spgConfigOut</structname>
+       C struct, which the function must fill with result data.
+<programlisting>
+typedef struct spgConfigIn
+{
+    Oid         attType;        /* Data type to be indexed */
+} spgConfigIn;
+
+typedef struct spgConfigOut
+{
+    Oid         prefixType;     /* Data type of inner-tuple prefixes */
+    Oid         labelType;      /* Data type of inner-tuple node labels */
+    Oid         leafType;       /* Data type of leaf-tuple values */
+    bool        canReturnData;  /* Opclass can reconstruct original data */
+    bool        longValuesOK;   /* Opclass can cope with values &gt; 1 page */
+} spgConfigOut;
+</programlisting>
+
+       <structfield>attType</structfield> is passed in order to support polymorphic
+       index operator classes; for ordinary fixed-data-type operator classes, it
+       will always have the same value and so can be ignored.
+      </para>
+
+      <para>
+       For operator classes that do not use prefixes,
+       <structfield>prefixType</structfield> can be set to <literal>VOIDOID</literal>.
+       Likewise, for operator classes that do not use node labels,
+       <structfield>labelType</structfield> can be set to <literal>VOIDOID</literal>.
+       <structfield>canReturnData</structfield> should be set true if the operator class
+       is capable of reconstructing the originally-supplied index value.
+       <structfield>longValuesOK</structfield> should be set true only when the
+       <structfield>attType</structfield> is of variable length and the operator
+       class is capable of segmenting long values by repeated suffixing
+       (see <xref linkend="spgist-limits"/>).
+      </para>
+
+      <para>
+       <structfield>leafType</structfield> should match the index storage type
+       defined by the operator class's <structfield>opckeytype</structfield>
+       catalog entry.
+       (Note that <structfield>opckeytype</structfield> can be zero,
+       implying the storage type is the same as the operator class's input
+       type, which is the most common situation.)
+       For reasons of backward compatibility, the <function>config</function>
+       method can set <structfield>leafType</structfield> to some other value,
+       and that value will be used; but this is deprecated since the index
+       contents are then incorrectly identified in the catalogs.
+       Also, it's permissible to
+       leave <structfield>leafType</structfield> uninitialized (zero);
+       that is interpreted as meaning the index storage type derived from
+       <structfield>opckeytype</structfield>.
+      </para>
+
+      <para>
+       When <structfield>attType</structfield>
+       and <structfield>leafType</structfield> are different, the optional
+       method <function>compress</function> must be provided.
+       Method <function>compress</function> is responsible
+       for transformation of datums to be indexed from <structfield>attType</structfield>
+       to <structfield>leafType</structfield>.
+      </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>choose</function></term>
+      <listitem>
+       <para>
+         Chooses a method for inserting a new value into an inner tuple.
+       </para>
+
+      <para>
+       The <acronym>SQL</acronym> declaration of the function must look like this:
+<programlisting>
+CREATE FUNCTION my_choose(internal, internal) RETURNS void ...
+</programlisting>
+       The first argument is a pointer to a <structname>spgChooseIn</structname>
+       C struct, containing input data for the function.
+       The second argument is a pointer to a <structname>spgChooseOut</structname>
+       C struct, which the function must fill with result data.
+<programlisting>
+typedef struct spgChooseIn
+{
+    Datum       datum;          /* original datum to be indexed */
+    Datum       leafDatum;      /* current datum to be stored at leaf */
+    int         level;          /* current level (counting from zero) */
+
+    /* Data from current inner tuple */
+    bool        allTheSame;     /* tuple is marked all-the-same? */
+    bool        hasPrefix;      /* tuple has a prefix? */
+    Datum       prefixDatum;    /* if so, the prefix value */
+    int         nNodes;         /* number of nodes in the inner tuple */
+    Datum      *nodeLabels;     /* node label values (NULL if none) */
+} spgChooseIn;
+
+typedef enum spgChooseResultType
+{
+    spgMatchNode = 1,           /* descend into existing node */
+    spgAddNode,                 /* add a node to the inner tuple */
+    spgSplitTuple               /* split inner tuple (change its prefix) */
+} spgChooseResultType;
+
+typedef struct spgChooseOut
+{
+    spgChooseResultType resultType;     /* action code, see above */
+    union
+    {
+        struct                  /* results for spgMatchNode */
+        {
+            int         nodeN;      /* descend to this node (index from 0) */
+            int         levelAdd;   /* increment level by this much */
+            Datum       restDatum;  /* new leaf datum */
+        }           matchNode;
+        struct                  /* results for spgAddNode */
+        {
+            Datum       nodeLabel;  /* new node's label */
+            int         nodeN;      /* where to insert it (index from 0) */
+        }           addNode;
+        struct                  /* results for spgSplitTuple */
+        {
+            /* Info to form new upper-level inner tuple with one child tuple */
+            bool        prefixHasPrefix;    /* tuple should have a prefix? */
+            Datum       prefixPrefixDatum;  /* if so, its value */
+            int         prefixNNodes;       /* number of nodes */
+            Datum      *prefixNodeLabels;   /* their labels (or NULL for
+                                             * no labels) */
+            int         childNodeN;         /* which node gets child tuple */
+
+            /* Info to form new lower-level inner tuple with all old nodes */
+            bool        postfixHasPrefix;   /* tuple should have a prefix? */
+            Datum       postfixPrefixDatum; /* if so, its value */
+        }           splitTuple;
+    }           result;
+} spgChooseOut;
+</programlisting>
+
+        <structfield>datum</structfield> is the original datum of
+        <structname>spgConfigIn</structname>.<structfield>attType</structfield>
+        type that was to be inserted into the index.
+        <structfield>leafDatum</structfield> is a value of
+        <structname>spgConfigOut</structname>.<structfield>leafType</structfield>
+        type, which is initially a result of method
+        <function>compress</function> applied to <structfield>datum</structfield>
+        when method <function>compress</function> is provided, or the same value as
+        <structfield>datum</structfield> otherwise.
+        <structfield>leafDatum</structfield> can change at lower levels of the tree
+        if the <function>choose</function> or <function>picksplit</function>
+        methods change it.  When the insertion search reaches a leaf page,
+        the current value of <structfield>leafDatum</structfield> is what will be stored
+        in the newly created leaf tuple.
+        <structfield>level</structfield> is the current inner tuple's level, starting at
+        zero for the root level.
+        <structfield>allTheSame</structfield> is true if the current inner tuple is
+        marked as containing multiple equivalent nodes
+        (see <xref linkend="spgist-all-the-same"/>).
+        <structfield>hasPrefix</structfield> is true if the current inner tuple contains
+        a prefix; if so,
+        <structfield>prefixDatum</structfield> is its value.
+        <structfield>nNodes</structfield> is the number of child nodes contained in the
+        inner tuple, and
+        <structfield>nodeLabels</structfield> is an array of their label values, or
+        NULL if there are no labels.
+       </para>
+
+       <para>
+        The <function>choose</function> function can determine either that
+        the new value matches one of the existing child nodes, or that a new
+        child node must be added, or that the new value is inconsistent with
+        the tuple prefix and so the inner tuple must be split to create a
+        less restrictive prefix.
+       </para>
+
+       <para>
+        If the new value matches one of the existing child nodes,
+        set <structfield>resultType</structfield> to <literal>spgMatchNode</literal>.
+        Set <structfield>nodeN</structfield> to the index (from zero) of that node in
+        the node array.
+        Set <structfield>levelAdd</structfield> to the increment in
+        <structfield>level</structfield> caused by descending through that node,
+        or leave it as zero if the operator class does not use levels.
+        Set <structfield>restDatum</structfield> to equal <structfield>leafDatum</structfield>
+        if the operator class does not modify datums from one level to the
+        next, or otherwise set it to the modified value to be used as
+        <structfield>leafDatum</structfield> at the next level.
+       </para>
+
+       <para>
+        If a new child node must be added,
+        set <structfield>resultType</structfield> to <literal>spgAddNode</literal>.
+        Set <structfield>nodeLabel</structfield> to the label to be used for the new
+        node, and set <structfield>nodeN</structfield> to the index (from zero) at which
+        to insert the node in the node array.
+        After the node has been added, the <function>choose</function>
+        function will be called again with the modified inner tuple;
+        that call should result in an <literal>spgMatchNode</literal> result.
+       </para>
+
+       <para>
+        If the new value is inconsistent with the tuple prefix,
+        set <structfield>resultType</structfield> to <literal>spgSplitTuple</literal>.
+        This action moves all the existing nodes into a new lower-level
+        inner tuple, and replaces the existing inner tuple with a tuple
+        having a single downlink pointing to the new lower-level inner tuple.
+        Set <structfield>prefixHasPrefix</structfield> to indicate whether the new
+        upper tuple should have a prefix, and if so set
+        <structfield>prefixPrefixDatum</structfield> to the prefix value.  This new
+        prefix value must be sufficiently less restrictive than the original
+        to accept the new value to be indexed.
+        Set <structfield>prefixNNodes</structfield> to the number of nodes needed in the
+        new tuple, and set <structfield>prefixNodeLabels</structfield> to a palloc'd array
+        holding their labels, or to NULL if node labels are not required.
+        Note that the total size of the new upper tuple must be no more
+        than the total size of the tuple it is replacing; this constrains
+        the lengths of the new prefix and new labels.
+        Set <structfield>childNodeN</structfield> to the index (from zero) of the node
+        that will downlink to the new lower-level inner tuple.
+        Set <structfield>postfixHasPrefix</structfield> to indicate whether the new
+        lower-level inner tuple should have a prefix, and if so set
+        <structfield>postfixPrefixDatum</structfield> to the prefix value.  The
+        combination of these two prefixes and the downlink node's label
+        (if any) must have the same meaning as the original prefix, because
+        there is no opportunity to alter the node labels that are moved to
+        the new lower-level tuple, nor to change any child index entries.
+        After the node has been split, the <function>choose</function>
+        function will be called again with the replacement inner tuple.
+        That call may return an <literal>spgAddNode</literal> result, if no suitable
+        node was created by the <literal>spgSplitTuple</literal> action.  Eventually
+        <function>choose</function> must return <literal>spgMatchNode</literal> to
+        allow the insertion to descend to the next level.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>picksplit</function></term>
+      <listitem>
+       <para>
+        Decides how to create a new inner tuple over a set of leaf tuples.
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+<programlisting>
+CREATE FUNCTION my_picksplit(internal, internal) RETURNS void ...
+</programlisting>
+       The first argument is a pointer to a <structname>spgPickSplitIn</structname>
+       C struct, containing input data for the function.
+       The second argument is a pointer to a <structname>spgPickSplitOut</structname>
+       C struct, which the function must fill with result data.
+<programlisting>
+typedef struct spgPickSplitIn
+{
+    int         nTuples;        /* number of leaf tuples */
+    Datum      *datums;         /* their datums (array of length nTuples) */
+    int         level;          /* current level (counting from zero) */
+} spgPickSplitIn;
+
+typedef struct spgPickSplitOut
+{
+    bool        hasPrefix;      /* new inner tuple should have a prefix? */
+    Datum       prefixDatum;    /* if so, its value */
+
+    int         nNodes;         /* number of nodes for new inner tuple */
+    Datum      *nodeLabels;     /* their labels (or NULL for no labels) */
+
+    int        *mapTuplesToNodes;   /* node index for each leaf tuple */
+    Datum      *leafTupleDatums;    /* datum to store in each new leaf tuple */
+} spgPickSplitOut;
+</programlisting>
+
+        <structfield>nTuples</structfield> is the number of leaf tuples provided.
+        <structfield>datums</structfield> is an array of their datum values of
+        <structname>spgConfigOut</structname>.<structfield>leafType</structfield>
+        type.
+        <structfield>level</structfield> is the current level that all the leaf tuples
+        share, which will become the level of the new inner tuple.
+       </para>
+
+       <para>
+        Set <structfield>hasPrefix</structfield> to indicate whether the new inner
+        tuple should have a prefix, and if so set
+        <structfield>prefixDatum</structfield> to the prefix value.
+        Set <structfield>nNodes</structfield> to indicate the number of nodes that
+        the new inner tuple will contain, and
+        set <structfield>nodeLabels</structfield> to an array of their label values,
+        or to NULL if node labels are not required.
+        Set <structfield>mapTuplesToNodes</structfield> to an array that gives the index
+        (from zero) of the node that each leaf tuple should be assigned to.
+        Set <structfield>leafTupleDatums</structfield> to an array of the values to
+        be stored in the new leaf tuples (these will be the same as the
+        input <structfield>datums</structfield> if the operator class does not modify
+        datums from one level to the next).
+        Note that the <function>picksplit</function> function is
+        responsible for palloc'ing the
+        <structfield>nodeLabels</structfield>, <structfield>mapTuplesToNodes</structfield> and
+        <structfield>leafTupleDatums</structfield> arrays.
+       </para>
+
+       <para>
+        If more than one leaf tuple is supplied, it is expected that the
+        <function>picksplit</function> function will classify them into more than
+        one node; otherwise it is not possible to split the leaf tuples
+        across multiple pages, which is the ultimate purpose of this
+        operation.  Therefore, if the <function>picksplit</function> function
+        ends up placing all the leaf tuples in the same node, the core
+        SP-GiST code will override that decision and generate an inner
+        tuple in which the leaf tuples are assigned at random to several
+        identically-labeled nodes.  Such a tuple is marked
+        <literal>allTheSame</literal> to signify that this has happened.  The
+        <function>choose</function> and <function>inner_consistent</function> functions
+        must take suitable care with such inner tuples.
+        See <xref linkend="spgist-all-the-same"/> for more information.
+       </para>
+
+       <para>
+        <function>picksplit</function> can be applied to a single leaf tuple only
+        in the case that the <function>config</function> function set
+        <structfield>longValuesOK</structfield> to true and a larger-than-a-page input
+        value has been supplied.  In this case the point of the operation is
+        to strip off a prefix and produce a new, shorter leaf datum value.
+        The call will be repeated until a leaf datum short enough to fit on
+        a page has been produced.  See <xref linkend="spgist-limits"/> for
+        more information.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>inner_consistent</function></term>
+      <listitem>
+       <para>
+        Returns set of nodes (branches) to follow during tree search.
+       </para>
+
+       <para>
+        The <acronym>SQL</acronym> declaration of the function must look like this:
+<programlisting>
+CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...
+</programlisting>
+       The first argument is a pointer to a <structname>spgInnerConsistentIn</structname>
+       C struct, containing input data for the function.
+       The second argument is a pointer to a <structname>spgInnerConsistentOut</structname>
+       C struct, which the function must fill with result data.
+
+<programlisting>
+typedef struct spgInnerConsistentIn
+{
+    ScanKey     scankeys;       /* array of operators and comparison values */
+    ScanKey     orderbys;       /* array of ordering operators and comparison
+                                 * values */
+    int         nkeys;          /* length of scankeys array */
+    int         norderbys;      /* length of orderbys array */
+
+    Datum       reconstructedValue;     /* value reconstructed at parent */
+    void       *traversalValue; /* opclass-specific traverse value */
+    MemoryContext traversalMemoryContext;   /* put new traverse values here */
+    int         level;          /* current level (counting from zero) */
+    bool        returnData;     /* original data must be returned? */
+
+    /* Data from current inner tuple */
+    bool        allTheSame;     /* tuple is marked all-the-same? */
+    bool        hasPrefix;      /* tuple has a prefix? */
+    Datum       prefixDatum;    /* if so, the prefix value */
+    int         nNodes;         /* number of nodes in the inner tuple */
+    Datum      *nodeLabels;     /* node label values (NULL if none) */
+} spgInnerConsistentIn;
+
+typedef struct spgInnerConsistentOut
+{
+    int         nNodes;         /* number of child nodes to be visited */
+    int        *nodeNumbers;    /* their indexes in the node array */
+    int        *levelAdds;      /* increment level by this much for each */
+    Datum      *reconstructedValues;    /* associated reconstructed values */
+    void      **traversalValues;        /* opclass-specific traverse values */
+    double    **distances;              /* associated distances */
+} spgInnerConsistentOut;
+</programlisting>
+
+        The array <structfield>scankeys</structfield>, of length <structfield>nkeys</structfield>,
+        describes the index search condition(s).  These conditions are
+        combined with AND &mdash; only index entries that satisfy all of
+        them are interesting.  (Note that <structfield>nkeys</structfield> = 0 implies
+        that all index entries satisfy the query.)  Usually the consistent
+        function only cares about the <structfield>sk_strategy</structfield> and
+        <structfield>sk_argument</structfield> fields of each array entry, which
+        respectively give the indexable operator and comparison value.
+        In particular it is not necessary to check <structfield>sk_flags</structfield> to
+        see if the comparison value is NULL, because the SP-GiST core code
+        will filter out such conditions.
+        The array <structfield>orderbys</structfield>, of length <structfield>norderbys</structfield>,
+        describes ordering operators (if any) in the same manner.
+        <structfield>reconstructedValue</structfield> is the value reconstructed for the
+        parent tuple; it is <literal>(Datum) 0</literal> at the root level or if the
+        <function>inner_consistent</function> function did not provide a value at the
+        parent level.
+        <structfield>traversalValue</structfield> is a pointer to any traverse data
+        passed down from the previous call of <function>inner_consistent</function>
+        on the parent index tuple, or NULL at the root level.
+        <structfield>traversalMemoryContext</structfield> is the memory context in which
+        to store output traverse values (see below).
+        <structfield>level</structfield> is the current inner tuple's level, starting at
+        zero for the root level.
+        <structfield>returnData</structfield> is <literal>true</literal> if reconstructed data is
+        required for this query; this will only be so if the
+        <function>config</function> function asserted <structfield>canReturnData</structfield>.
+        <structfield>allTheSame</structfield> is true if the current inner tuple is
+        marked <quote>all-the-same</quote>; in this case all the nodes have the
+        same label (if any) and so either all or none of them match the query
+        (see <xref linkend="spgist-all-the-same"/>).
+        <structfield>hasPrefix</structfield> is true if the current inner tuple contains
+        a prefix; if so,
+        <structfield>prefixDatum</structfield> is its value.
+        <structfield>nNodes</structfield> is the number of child nodes contained in the
+        inner tuple, and
+        <structfield>nodeLabels</structfield> is an array of their label values, or
+        NULL if the nodes do not have labels.
+       </para>
+
+       <para>
+        <structfield>nNodes</structfield> must be set to the number of child nodes that
+        need to be visited by the search, and
+        <structfield>nodeNumbers</structfield> must be set to an array of their indexes.
+        If the operator class keeps track of levels, set
+        <structfield>levelAdds</structfield> to an array of the level increments
+        required when descending to each node to be visited.  (Often these
+        increments will be the same for all the nodes, but that's not
+        necessarily so, so an array is used.)
+        If value reconstruction is needed, set
+        <structfield>reconstructedValues</structfield> to an array of the values
+        reconstructed for each child node to be visited; otherwise, leave
+        <structfield>reconstructedValues</structfield> as NULL.
+        The reconstructed values are assumed to be of type
+        <structname>spgConfigOut</structname>.<structfield>leafType</structfield>.
+        (However, since the core system will do nothing with them except
+        possibly copy them, it is sufficient for them to have the
+        same <literal>typlen</literal> and <literal>typbyval</literal>
+        properties as <structfield>leafType</structfield>.)
+        If ordered search is performed, set <structfield>distances</structfield>
+        to an array of distance values according to <structfield>orderbys</structfield>
+        array (nodes with lowest distances will be processed first).  Leave it
+        NULL otherwise.
+        If it is desired to pass down additional out-of-band information
+        (<quote>traverse values</quote>) to lower levels of the tree search,
+        set <structfield>traversalValues</structfield> to an array of the appropriate
+        traverse values, one for each child node to be visited; otherwise,
+        leave <structfield>traversalValues</structfield> as NULL.
+        Note that the <function>inner_consistent</function> function is
+        responsible for palloc'ing the
+        <structfield>nodeNumbers</structfield>, <structfield>levelAdds</structfield>,
+        <structfield>distances</structfield>,
+        <structfield>reconstructedValues</structfield>, and
+        <structfield>traversalValues</structfield> arrays in the current memory context.
+        However, any output traverse values pointed to by
+        the <structfield>traversalValues</structfield> array should be allocated
+        in <structfield>traversalMemoryContext</structfield>.
+        Each traverse value must be a single palloc'd chunk.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>leaf_consistent</function></term>
+      <listitem>
+       <para>
+        Returns true if a leaf tuple satisfies a query.
+       </para>
+
+       <para>
+        The <acronym>SQL</acronym> declaration of the function must look like this:
+<programlisting>
+CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ...
+</programlisting>
+       The first argument is a pointer to a <structname>spgLeafConsistentIn</structname>
+       C struct, containing input data for the function.
+       The second argument is a pointer to a <structname>spgLeafConsistentOut</structname>
+       C struct, which the function must fill with result data.
+<programlisting>
+typedef struct spgLeafConsistentIn
+{
+    ScanKey     scankeys;       /* array of operators and comparison values */
+    ScanKey     orderbys;       /* array of ordering operators and comparison
+                                 * values */
+    int         nkeys;          /* length of scankeys array */
+    int         norderbys;      /* length of orderbys array */
+
+    Datum       reconstructedValue;     /* value reconstructed at parent */
+    void       *traversalValue; /* opclass-specific traverse value */
+    int         level;          /* current level (counting from zero) */
+    bool        returnData;     /* original data must be returned? */
+
+    Datum       leafDatum;      /* datum in leaf tuple */
+} spgLeafConsistentIn;
+
+typedef struct spgLeafConsistentOut
+{
+    Datum       leafValue;        /* reconstructed original data, if any */
+    bool        recheck;          /* set true if operator must be rechecked */
+    bool        recheckDistances; /* set true if distances must be rechecked */
+    double     *distances;        /* associated distances */
+} spgLeafConsistentOut;
+</programlisting>
+
+        The array <structfield>scankeys</structfield>, of length <structfield>nkeys</structfield>,
+        describes the index search condition(s).  These conditions are
+        combined with AND &mdash; only index entries that satisfy all of
+        them satisfy the query.  (Note that <structfield>nkeys</structfield> = 0 implies
+        that all index entries satisfy the query.)  Usually the consistent
+        function only cares about the <structfield>sk_strategy</structfield> and
+        <structfield>sk_argument</structfield> fields of each array entry, which
+        respectively give the indexable operator and comparison value.
+        In particular it is not necessary to check <structfield>sk_flags</structfield> to
+        see if the comparison value is NULL, because the SP-GiST core code
+        will filter out such conditions.
+        The array <structfield>orderbys</structfield>, of length <structfield>norderbys</structfield>,
+        describes the ordering operators in the same manner.
+        <structfield>reconstructedValue</structfield> is the value reconstructed for the
+        parent tuple; it is <literal>(Datum) 0</literal> at the root level or if the
+        <function>inner_consistent</function> function did not provide a value at the
+        parent level.
+        <structfield>traversalValue</structfield> is a pointer to any traverse data
+        passed down from the previous call of <function>inner_consistent</function>
+        on the parent index tuple, or NULL at the root level.
+        <structfield>level</structfield> is the current leaf tuple's level, starting at
+        zero for the root level.
+        <structfield>returnData</structfield> is <literal>true</literal> if reconstructed data is
+        required for this query; this will only be so if the
+        <function>config</function> function asserted <structfield>canReturnData</structfield>.
+        <structfield>leafDatum</structfield> is the key value of
+        <structname>spgConfigOut</structname>.<structfield>leafType</structfield>
+        stored in the current leaf tuple.
+       </para>
+
+       <para>
+        The function must return <literal>true</literal> if the leaf tuple matches the
+        query, or <literal>false</literal> if not.  In the <literal>true</literal> case,
+        if <structfield>returnData</structfield> is <literal>true</literal> then
+        <structfield>leafValue</structfield> must be set to the value (of type
+        <structname>spgConfigIn</structname>.<structfield>attType</structfield>)
+        originally supplied to be indexed for this leaf tuple.  Also,
+        <structfield>recheck</structfield> may be set to <literal>true</literal> if the match
+        is uncertain and so the operator(s) must be re-applied to the actual
+        heap tuple to verify the match.
+        If ordered search is performed, set <structfield>distances</structfield>
+        to an array of distance values according to <structfield>orderbys</structfield>
+        array.  Leave it NULL otherwise.  If at least one of returned distances
+        is not exact, set <structfield>recheckDistances</structfield> to true.
+        In this case, the executor will calculate the exact distances after
+        fetching the tuple from the heap, and will reorder the tuples if needed.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+
+  <para>
+   The optional user-defined methods are:
+  </para>
+
+  <variablelist>
+     <varlistentry>
+      <term><function>Datum compress(Datum in)</function></term>
+      <listitem>
+       <para>
+        Converts a data item into a format suitable for physical storage in
+        a leaf tuple of the index.  It accepts a value of type
+        <structname>spgConfigIn</structname>.<structfield>attType</structfield>
+        and returns a value of type
+        <structname>spgConfigOut</structname>.<structfield>leafType</structfield>.
+        The output value must not contain an out-of-line TOAST pointer.
+       </para>
+
+       <para>
+        Note: the <function>compress</function> method is only applied to
+        values to be stored.  The consistent methods receive query
+        <structfield>scankeys</structfield> unchanged, without transformation
+        using <function>compress</function>.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>options</function></term>
+      <listitem>
+       <para>
+        Defines a set of user-visible parameters that control operator class
+        behavior.
+       </para>
+
+       <para>
+         The <acronym>SQL</acronym> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_options(internal)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+       </para>
+
+       <para>
+        The function is passed a pointer to a <structname>local_relopts</structname>
+        struct, which needs to be filled with a set of operator class
+        specific options.  The options can be accessed from other support
+        functions using the <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
+        <literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
+       </para>
+
+       <para>
+        Since the representation of the key in <acronym>SP-GiST</acronym> is
+        flexible, it may depend on user-specified parameters.
+       </para>
+      </listitem>
+     </varlistentry>
+   </variablelist>
+
+   <para>
+    All the SP-GiST support methods are normally called in a short-lived
+    memory context; that is, <varname>CurrentMemoryContext</varname> will be reset
+    after processing of each tuple.  It is therefore not very important to
+    worry about pfree'ing everything you palloc.  (The <function>config</function>
+    method is an exception: it should try to avoid leaking memory.  But
+    usually the <function>config</function> method need do nothing but assign
+    constants into the passed parameter struct.)
+   </para>
+
+   <para>
+    If the indexed column is of a collatable data type, the index collation
+    will be passed to all the support methods, using the standard
+    <function>PG_GET_COLLATION()</function> mechanism.
+   </para>
+
+ </sect2>
+
+ <sect2 id="spgist-implementation">
+  <title>Implementation</title>
+
+   <para>
+    This section covers implementation details and other tricks that are
+    useful for implementers of <acronym>SP-GiST</acronym> operator classes to
+    know.
+   </para>
+
+  <sect3 id="spgist-limits">
+   <title>SP-GiST Limits</title>
+
+   <para>
+    Individual leaf tuples and inner tuples must fit on a single index page
+    (8kB by default).  Therefore, when indexing values of variable-length
+    data types, long values can only be supported by methods such as radix
+    trees, in which each level of the tree includes a prefix that is short
+    enough to fit on a page, and the final leaf level includes a suffix also
+    short enough to fit on a page.  The operator class should set
+    <structfield>longValuesOK</structfield> to true only if it is prepared to arrange for
+    this to happen.  Otherwise, the <acronym>SP-GiST</acronym> core will
+    reject any request to index a value that is too large to fit
+    on an index page.
+   </para>
+
+   <para>
+    Likewise, it is the operator class's responsibility that inner tuples
+    do not grow too large to fit on an index page; this limits the number
+    of child nodes that can be used in one inner tuple, as well as the
+    maximum size of a prefix value.
+   </para>
+
+   <para>
+    Another limitation is that when an inner tuple's node points to a set
+    of leaf tuples, those tuples must all be in the same index page.
+    (This is a design decision to reduce seeking and save space in the
+    links that chain such tuples together.)  If the set of leaf tuples
+    grows too large for a page, a split is performed and an intermediate
+    inner tuple is inserted.  For this to fix the problem, the new inner
+    tuple <emphasis>must</emphasis> divide the set of leaf values into more than one
+    node group.  If the operator class's <function>picksplit</function> function
+    fails to do that, the <acronym>SP-GiST</acronym> core resorts to
+    extraordinary measures described in <xref linkend="spgist-all-the-same"/>.
+   </para>
+
+   <para>
+    When <structfield>longValuesOK</structfield> is true, it is expected
+    that successive levels of the <acronym>SP-GiST</acronym> tree will
+    absorb more and more information into the prefixes and node labels of
+    the inner tuples, making the required leaf datum smaller and smaller,
+    so that eventually it will fit on a page.
+    To prevent bugs in operator classes from causing infinite insertion
+    loops, the <acronym>SP-GiST</acronym> core will raise an error if the
+    leaf datum does not become any smaller within ten cycles
+    of <function>choose</function> method calls.
+   </para>
+  </sect3>
+
+  <sect3 id="spgist-null-labels">
+   <title>SP-GiST Without Node Labels</title>
+
+   <para>
+    Some tree algorithms use a fixed set of nodes for each inner tuple;
+    for example, in a quad-tree there are always exactly four nodes
+    corresponding to the four quadrants around the inner tuple's centroid
+    point.  In such a case the code typically works with the nodes by
+    number, and there is no need for explicit node labels.  To suppress
+    node labels (and thereby save some space), the <function>picksplit</function>
+    function can return NULL for the <structfield>nodeLabels</structfield> array,
+    and likewise the <function>choose</function> function can return NULL for
+    the <structfield>prefixNodeLabels</structfield> array during
+    a <literal>spgSplitTuple</literal> action.
+    This will in turn result in <structfield>nodeLabels</structfield> being NULL during
+    subsequent calls to <function>choose</function> and <function>inner_consistent</function>.
+    In principle, node labels could be used for some inner tuples and omitted
+    for others in the same index.
+   </para>
+
+   <para>
+    When working with an inner tuple having unlabeled nodes, it is an error
+    for <function>choose</function> to return <literal>spgAddNode</literal>, since the set
+    of nodes is supposed to be fixed in such cases.
+   </para>
+  </sect3>
+
+  <sect3 id="spgist-all-the-same">
+   <title><quote>All-the-Same</quote> Inner Tuples</title>
+
+   <para>
+    The <acronym>SP-GiST</acronym> core can override the results of the
+    operator class's <function>picksplit</function> function when
+    <function>picksplit</function> fails to divide the supplied leaf values into
+    at least two node categories.  When this happens, the new inner tuple
+    is created with multiple nodes that each have the same label (if any)
+    that <function>picksplit</function> gave to the one node it did use, and the
+    leaf values are divided at random among these equivalent nodes.
+    The <literal>allTheSame</literal> flag is set on the inner tuple to warn the
+    <function>choose</function> and <function>inner_consistent</function> functions that the
+    tuple does not have the node set that they might otherwise expect.
+   </para>
+
+   <para>
+    When dealing with an <literal>allTheSame</literal> tuple, a <function>choose</function>
+    result of <literal>spgMatchNode</literal> is interpreted to mean that the new
+    value can be assigned to any of the equivalent nodes; the core code will
+    ignore the supplied  <structfield>nodeN</structfield> value and descend into one
+    of the nodes at random (so as to keep the tree balanced).  It is an
+    error for <function>choose</function> to return <literal>spgAddNode</literal>, since
+    that would make the nodes not all equivalent; the
+    <literal>spgSplitTuple</literal> action must be used if the value to be inserted
+    doesn't match the existing nodes.
+   </para>
+
+   <para>
+    When dealing with an <literal>allTheSame</literal> tuple, the
+    <function>inner_consistent</function> function should return either all or none
+    of the nodes as targets for continuing the index search, since they are
+    all equivalent.  This may or may not require any special-case code,
+    depending on how much the <function>inner_consistent</function> function normally
+    assumes about the meaning of the nodes.
+   </para>
+  </sect3>
+
+ </sect2>
+
+ <sect2 id="spgist-examples">
+  <title>Examples</title>
+
+  <para>
+   The <productname>PostgreSQL</productname> source distribution includes
+   several examples of index operator classes for <acronym>SP-GiST</acronym>,
+   as described in <xref linkend="spgist-builtin-opclasses-table"/>.  Look
+   into <filename>src/backend/access/spgist/</filename>
+   and <filename>src/backend/utils/adt/</filename> to see the code.
+  </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="gin">
+ <title>GIN Indexes</title>
+
+    <indexterm>
+     <primary>index</primary>
+     <secondary>GIN</secondary>
+    </indexterm>
+
+ <sect2 id="gin-intro">
+  <title>Introduction</title>
+
+  <para>
+   <acronym>GIN</acronym> stands for Generalized Inverted Index.
+   <acronym>GIN</acronym> is designed for handling cases where the items
+   to be indexed are composite values, and the queries to be handled by
+   the index need to search for element values that appear within
+   the composite items.  For example, the items could be documents,
+   and the queries could be searches for documents containing specific words.
+  </para>
+
+  <para>
+   We use the word <firstterm>item</firstterm> to refer to a composite value that
+   is to be indexed, and the word <firstterm>key</firstterm> to refer to an element
+   value.  <acronym>GIN</acronym> always stores and searches for keys,
+   not item values per se.
+  </para>
+
+  <para>
+   A <acronym>GIN</acronym> index stores a set of (key, posting list) pairs,
+   where a <firstterm>posting list</firstterm> is a set of row IDs in which the key
+   occurs.  The same row ID can appear in multiple posting lists, since
+   an item can contain more than one key.  Each key value is stored only
+   once, so a <acronym>GIN</acronym> index is very compact for cases
+   where the same key appears many times.
+  </para>
+
+  <para>
+   <acronym>GIN</acronym> is generalized in the sense that the
+   <acronym>GIN</acronym> access method code does not need to know the
+   specific operations that it accelerates.
+   Instead, it uses custom strategies defined for particular data types.
+   The strategy defines how keys are extracted from indexed items and
+   query conditions, and how to determine whether a row that contains
+   some of the key values in a query actually satisfies the query.
+  </para>
+
+  <para>
+   One advantage of <acronym>GIN</acronym> is that it allows the development
+   of custom data types with the appropriate access methods, by
+   an expert in the domain of the data type, rather than a database expert.
+   This is much the same advantage as using <acronym>GiST</acronym>.
+  </para>
+
+  <para>
+   The <acronym>GIN</acronym>
+   implementation in <productname>PostgreSQL</productname> is primarily
+   maintained by Teodor Sigaev and Oleg Bartunov. There is more
+   information about <acronym>GIN</acronym> on their
+   <ulink url="http://www.sai.msu.su/~megera/wiki/Gin">website</ulink>.
+  </para>
+ </sect2>
+
+ <sect2 id="gin-builtin-opclasses">
+  <title>Built-in Operator Classes</title>
+
+  <para>
+   The core <productname>PostgreSQL</productname> distribution
+   includes the <acronym>GIN</acronym> operator classes shown in
+   <xref linkend="gin-builtin-opclasses-table"/>.
+   (Some of the optional modules described in <xref linkend="contrib"/>
+   provide additional <acronym>GIN</acronym> operator classes.)
+  </para>
+
+   <table id="gin-builtin-opclasses-table">
+    <title>Built-in <acronym>GIN</acronym> Operator Classes</title>
+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Name</entry>
+       <entry>Indexable Operators</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry morerows="3" valign="middle"><literal>array_ops</literal></entry>
+       <entry><literal>&amp;&amp; (anyarray,anyarray)</literal></entry>
+      </row>
+      <row>
+       <entry><literal>@&gt; (anyarray,anyarray)</literal></entry>
+      </row>
+      <row>
+       <entry><literal>&lt;@ (anyarray,anyarray)</literal></entry>
+      </row>
+      <row>
+       <entry><literal>= (anyarray,anyarray)</literal></entry>
+      </row>
+      <row>
+       <entry morerows="5" valign="middle"><literal>jsonb_ops</literal></entry>
+       <entry><literal>@&gt; (jsonb,jsonb)</literal></entry>
+      </row>
+      <row>
+       <entry><literal>@? (jsonb,jsonpath)</literal></entry>
+      </row>
+      <row>
+       <entry><literal>@@ (jsonb,jsonpath)</literal></entry>
+      </row>
+      <row>
+       <entry><literal>? (jsonb,text)</literal></entry>
+      </row>
+      <row>
+       <entry><literal>?| (jsonb,text[])</literal></entry>
+      </row>
+      <row>
+       <entry><literal>?&amp; (jsonb,text[])</literal></entry>
+      </row>
+      <row>
+       <entry morerows="2" valign="middle"><literal>jsonb_path_ops</literal></entry>
+       <entry><literal>@&gt; (jsonb,jsonb)</literal></entry>
+      </row>
+      <row>
+       <entry><literal>@? (jsonb,jsonpath)</literal></entry>
+      </row>
+      <row>
+       <entry><literal>@@ (jsonb,jsonpath)</literal></entry>
+      </row>
+      <row>
+       <entry valign="middle"><literal>tsvector_ops</literal></entry>
+       <entry><literal>@@ (tsvector,tsquery)</literal></entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   Of the two operator classes for type <type>jsonb</type>, <literal>jsonb_ops</literal>
+   is the default.  <literal>jsonb_path_ops</literal> supports fewer operators but
+   offers better performance for those operators.
+   See <xref linkend="json-indexing"/> for details.
+  </para>
+
+ </sect2>
+
+ <sect2 id="gin-extensibility">
+  <title>Extensibility</title>
+
+  <para>
+    The <acronym>GIN</acronym> interface has a high level of abstraction,
+    requiring the access method implementer only to implement the semantics of
+    the data type being accessed.  The <acronym>GIN</acronym> layer itself
+    takes care of concurrency, logging and searching the tree structure.
+  </para>
+
+  <para>
+    All it takes to get a <acronym>GIN</acronym> access method working is to
+    implement a few user-defined methods, which define the behavior of
+    keys in the tree and the relationships between keys, indexed items,
+    and indexable queries. In short, <acronym>GIN</acronym> combines
+    extensibility with generality, code reuse, and a clean interface.
+  </para>
+
+  <para>
+    There are two methods that an operator class for
+    <acronym>GIN</acronym> must provide:
+
+   <variablelist>
+     <varlistentry>
+      <term><function>Datum *extractValue(Datum itemValue, int32 *nkeys,
+         bool **nullFlags)</function></term>
+      <listitem>
+       <para>
+        Returns a palloc'd array of keys given an item to be indexed.  The
+        number of returned keys must be stored into <literal>*nkeys</literal>.
+        If any of the keys can be null, also palloc an array of
+        <literal>*nkeys</literal> <type>bool</type> fields, store its address at
+        <literal>*nullFlags</literal>, and set these null flags as needed.
+        <literal>*nullFlags</literal> can be left <symbol>NULL</symbol> (its initial value)
+        if all keys are non-null.
+        The return value can be <symbol>NULL</symbol> if the item contains no keys.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>Datum *extractQuery(Datum query, int32 *nkeys,
+         StrategyNumber n, bool **pmatch, Pointer **extra_data,
+         bool **nullFlags, int32 *searchMode)</function></term>
+      <listitem>
+       <para>
+        Returns a palloc'd array of keys given a value to be queried; that is,
+        <literal>query</literal> is the value on the right-hand side of an
+        indexable operator whose left-hand side is the indexed column.
+        <literal>n</literal> is the strategy number of the operator within the
+        operator class (see <xref linkend="xindex-strategies"/>).
+        Often, <function>extractQuery</function> will need
+        to consult <literal>n</literal> to determine the data type of
+        <literal>query</literal> and the method it should use to extract key values.
+        The number of returned keys must be stored into <literal>*nkeys</literal>.
+        If any of the keys can be null, also palloc an array of
+        <literal>*nkeys</literal> <type>bool</type> fields, store its address at
+        <literal>*nullFlags</literal>, and set these null flags as needed.
+        <literal>*nullFlags</literal> can be left <symbol>NULL</symbol> (its initial value)
+        if all keys are non-null.
+        The return value can be <symbol>NULL</symbol> if the <literal>query</literal> contains no keys.
+       </para>
+
+       <para>
+        <literal>searchMode</literal> is an output argument that allows
+        <function>extractQuery</function> to specify details about how the search
+        will be done.
+        If <literal>*searchMode</literal> is set to
+        <literal>GIN_SEARCH_MODE_DEFAULT</literal> (which is the value it is
+        initialized to before call), only items that match at least one of
+        the returned keys are considered candidate matches.
+        If <literal>*searchMode</literal> is set to
+        <literal>GIN_SEARCH_MODE_INCLUDE_EMPTY</literal>, then in addition to items
+        containing at least one matching key, items that contain no keys at
+        all are considered candidate matches.  (This mode is useful for
+        implementing is-subset-of operators, for example.)
+        If <literal>*searchMode</literal> is set to <literal>GIN_SEARCH_MODE_ALL</literal>,
+        then all non-null items in the index are considered candidate
+        matches, whether they match any of the returned keys or not.  (This
+        mode is much slower than the other two choices, since it requires
+        scanning essentially the entire index, but it may be necessary to
+        implement corner cases correctly.  An operator that needs this mode
+        in most cases is probably not a good candidate for a GIN operator
+        class.)
+        The symbols to use for setting this mode are defined in
+        <filename>access/gin.h</filename>.
+       </para>
+
+       <para>
+        <literal>pmatch</literal> is an output argument for use when partial match
+        is supported.  To use it, <function>extractQuery</function> must allocate
+        an array of <literal>*nkeys</literal> <type>bool</type>s and store its address at
+        <literal>*pmatch</literal>.  Each element of the array should be set to true
+        if the corresponding key requires partial match, false if not.
+        If <literal>*pmatch</literal> is set to <symbol>NULL</symbol> then GIN assumes partial match
+        is not required.  The variable is initialized to <symbol>NULL</symbol> before call,
+        so this argument can simply be ignored by operator classes that do
+        not support partial match.
+       </para>
+
+       <para>
+        <literal>extra_data</literal> is an output argument that allows
+        <function>extractQuery</function> to pass additional data to the
+        <function>consistent</function> and <function>comparePartial</function> methods.
+        To use it, <function>extractQuery</function> must allocate
+        an array of <literal>*nkeys</literal> pointers and store its address at
+        <literal>*extra_data</literal>, then store whatever it wants to into the
+        individual pointers.  The variable is initialized to <symbol>NULL</symbol> before
+        call, so this argument can simply be ignored by operator classes that
+        do not require extra data.  If <literal>*extra_data</literal> is set, the
+        whole array is passed to the <function>consistent</function> method, and
+        the appropriate element to the <function>comparePartial</function> method.
+       </para>
+
+      </listitem>
+     </varlistentry>
+   </variablelist>
+
+   An operator class must also provide a function to check if an indexed item
+   matches the query. It comes in two flavors, a Boolean <function>consistent</function>
+   function, and a ternary <function>triConsistent</function> function.
+   <function>triConsistent</function> covers the functionality of both, so providing
+   <function>triConsistent</function> alone is sufficient. However, if the Boolean
+   variant is significantly cheaper to calculate, it can be advantageous to
+   provide both.  If only the Boolean variant is provided, some optimizations
+   that depend on refuting index items before fetching all the keys are
+   disabled.
+
+   <variablelist>
+     <varlistentry>
+      <term><function>bool consistent(bool check[], StrategyNumber n, Datum query,
+         int32 nkeys, Pointer extra_data[], bool *recheck,
+         Datum queryKeys[], bool nullFlags[])</function></term>
+      <listitem>
+       <para>
+        Returns true if an indexed item satisfies the query operator with
+        strategy number <literal>n</literal> (or might satisfy it, if the recheck
+        indication is returned).  This function does not have direct access
+        to the indexed item's value, since <acronym>GIN</acronym> does not
+        store items explicitly.  Rather, what is available is knowledge
+        about which key values extracted from the query appear in a given
+        indexed item.  The <literal>check</literal> array has length
+        <literal>nkeys</literal>, which is the same as the number of keys previously
+        returned by <function>extractQuery</function> for this <literal>query</literal> datum.
+        Each element of the
+        <literal>check</literal> array is true if the indexed item contains the
+        corresponding query key, i.e., if (check[i] == true) the i-th key of the
+        <function>extractQuery</function> result array is present in the indexed item.
+        The original <literal>query</literal> datum is
+        passed in case the <function>consistent</function> method needs to consult it,
+        and so are the <literal>queryKeys[]</literal> and <literal>nullFlags[]</literal>
+        arrays previously returned by <function>extractQuery</function>.
+        <literal>extra_data</literal> is the extra-data array returned by
+        <function>extractQuery</function>, or <symbol>NULL</symbol> if none.
+       </para>
+
+       <para>
+        When <function>extractQuery</function> returns a null key in
+        <literal>queryKeys[]</literal>, the corresponding <literal>check[]</literal> element
+        is true if the indexed item contains a null key; that is, the
+        semantics of <literal>check[]</literal> are like <literal>IS NOT DISTINCT
+        FROM</literal>.  The <function>consistent</function> function can examine the
+        corresponding <literal>nullFlags[]</literal> element if it needs to tell
+        the difference between a regular value match and a null match.
+       </para>
+
+       <para>
+        On success, <literal>*recheck</literal> should be set to true if the heap
+        tuple needs to be rechecked against the query operator, or false if
+        the index test is exact.  That is, a false return value guarantees
+        that the heap tuple does not match the query; a true return value with
+        <literal>*recheck</literal> set to false guarantees that the heap tuple does
+        match the query; and a true return value with
+        <literal>*recheck</literal> set to true means that the heap tuple might match
+        the query, so it needs to be fetched and rechecked by evaluating the
+        query operator directly against the originally indexed item.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><function>GinTernaryValue triConsistent(GinTernaryValue check[], StrategyNumber n, Datum query,
+         int32 nkeys, Pointer extra_data[],
+         Datum queryKeys[], bool nullFlags[])</function></term>
+      <listitem>
+       <para>
+        <function>triConsistent</function> is similar to <function>consistent</function>,
+        but instead of Booleans in the <literal>check</literal> vector, there are
+        three possible values for each
+        key: <literal>GIN_TRUE</literal>, <literal>GIN_FALSE</literal> and
+        <literal>GIN_MAYBE</literal>. <literal>GIN_FALSE</literal> and <literal>GIN_TRUE</literal>
+        have the same meaning as regular Boolean values, while
+        <literal>GIN_MAYBE</literal> means that the presence of that key is not known.
+        When <literal>GIN_MAYBE</literal> values are present, the function should only
+        return <literal>GIN_TRUE</literal> if the item certainly matches whether or
+        not the index item contains the corresponding query keys. Likewise, the
+        function must return <literal>GIN_FALSE</literal> only if the item certainly
+        does not match, whether or not it contains the <literal>GIN_MAYBE</literal>
+        keys. If the result depends on the <literal>GIN_MAYBE</literal> entries, i.e.,
+        the match cannot be confirmed or refuted based on the known query keys,
+        the function must return <literal>GIN_MAYBE</literal>.
+       </para>
+       <para>
+        When there are no <literal>GIN_MAYBE</literal> values in the <literal>check</literal>
+        vector, a <literal>GIN_MAYBE</literal> return value is the equivalent of
+        setting the <literal>recheck</literal> flag in the
+        Boolean <function>consistent</function> function.
+       </para>
+      </listitem>
+     </varlistentry>
+   </variablelist>
+  </para>
+
+  <para>
+   In addition, GIN must have a way to sort the key values stored in the index.
+   The operator class can define the sort ordering by specifying a comparison
+   method:
+
+   <variablelist>
+     <varlistentry>
+      <term><function>int compare(Datum a, Datum b)</function></term>
+      <listitem>
+       <para>
+        Compares two keys (not indexed items!) and returns an integer less than
+        zero, zero, or greater than zero, indicating whether the first key is
+        less than, equal to, or greater than the second.  Null keys are never
+        passed to this function.
+       </para>
+      </listitem>
+     </varlistentry>
+   </variablelist>
+
+   Alternatively, if the operator class does not provide a <function>compare</function>
+   method, GIN will look up the default btree operator class for the index
+   key data type, and use its comparison function.  It is recommended to
+   specify the comparison function in a GIN operator class that is meant for
+   just one data type, as looking up the btree operator class costs a few
+   cycles.  However, polymorphic GIN operator classes (such
+   as <literal>array_ops</literal>) typically cannot specify a single comparison
+   function.
+  </para>
+
+  <para>
+   An operator class for <acronym>GIN</acronym> can optionally supply the
+   following methods:
+
+   <variablelist>
+     <varlistentry>
+      <term><function>int comparePartial(Datum partial_key, Datum key, StrategyNumber n,
+                               Pointer extra_data)</function></term>
+      <listitem>
+       <para>
+        Compare a partial-match query key to an index key.  Returns an integer
+        whose sign indicates the result: less than zero means the index key
+        does not match the query, but the index scan should continue; zero
+        means that the index key does match the query; greater than zero
+        indicates that the index scan should stop because no more matches
+        are possible.  The strategy number <literal>n</literal> of the operator
+        that generated the partial match query is provided, in case its
+        semantics are needed to determine when to end the scan.  Also,
+        <literal>extra_data</literal> is the corresponding element of the extra-data
+        array made by <function>extractQuery</function>, or <symbol>NULL</symbol> if none.
+        Null keys are never passed to this function.
+       </para>
+      </listitem>
+     </varlistentry>
+     <varlistentry>
+      <term><function>void options(local_relopts *relopts)</function></term>
+      <listitem>
+       <para>
+        Defines a set of user-visible parameters that control operator class
+        behavior.
+       </para>
+
+       <para>
+        The <function>options</function> function is passed a pointer to a
+        <structname>local_relopts</structname> struct, which needs to be
+        filled with a set of operator class specific options.  The options
+        can be accessed from other support functions using the
+        <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
+        <literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
+       </para>
+
+       <para>
+        Since both key extraction of indexed values and representation of the
+        key in <acronym>GIN</acronym> are flexible, they may depend on
+        user-specified parameters.
+       </para>
+      </listitem>
+     </varlistentry>
+   </variablelist>
+  </para>
+
+  <para>
+   To support <quote>partial match</quote> queries, an operator class must
+   provide the <function>comparePartial</function> method, and its
+   <function>extractQuery</function> method must set the <literal>pmatch</literal>
+   parameter when a partial-match query is encountered.  See
+   <xref linkend="gin-partial-match"/> for details.
+  </para>
+
+  <para>
+   The actual data types of the various <literal>Datum</literal> values mentioned
+   above vary depending on the operator class.  The item values passed to
+   <function>extractValue</function> are always of the operator class's input type, and
+   all key values must be of the class's <literal>STORAGE</literal> type.  The type of
+   the <literal>query</literal> argument passed to <function>extractQuery</function>,
+   <function>consistent</function> and <function>triConsistent</function> is whatever is the
+   right-hand input type of the class member operator identified by the
+   strategy number.  This need not be the same as the indexed type, so long as
+   key values of the correct type can be extracted from it.  However, it is
+   recommended that the SQL declarations of these three support functions use
+   the opclass's indexed data type for the <literal>query</literal> argument, even
+   though the actual type might be something else depending on the operator.
+  </para>
+
+ </sect2>
+
+ <sect2 id="gin-implementation">
+  <title>Implementation</title>
+
+  <para>
+   Internally, a <acronym>GIN</acronym> index contains a B-tree index
+   constructed over keys, where each key is an element of one or more indexed
+   items (a member of an array, for example) and where each tuple in a leaf
+   page contains either a pointer to a B-tree of heap pointers (a
+   <quote>posting tree</quote>), or a simple list of heap pointers (a <quote>posting
+   list</quote>) when the list is small enough to fit into a single index tuple along
+   with the key value.  <xref linkend="gin-internals-figure"/> illustrates
+   these components of a GIN index.
+  </para>
+
+  <para>
+   As of <productname>PostgreSQL</productname> 9.1, null key values can be
+   included in the index.  Also, placeholder nulls are included in the index
+   for indexed items that are null or contain no keys according to
+   <function>extractValue</function>.  This allows searches that should find empty
+   items to do so.
+  </para>
+
+  <para>
+   Multicolumn <acronym>GIN</acronym> indexes are implemented by building
+   a single B-tree over composite values (column number, key value).  The
+   key values for different columns can be of different types.
+  </para>
+
+  <figure id="gin-internals-figure">
+   <title>GIN Internals</title>
+   <mediaobject>
+    <imageobject>
+     <imagedata fileref="images/gin.svg" format="SVG" width="100%"/>
+    </imageobject>
+   </mediaobject>
+  </figure>
+
+  <sect3 id="gin-fast-update">
+   <title>GIN Fast Update Technique</title>
+
+   <para>
+    Updating a <acronym>GIN</acronym> index tends to be slow because of the
+    intrinsic nature of inverted indexes: inserting or updating one heap row
+    can cause many inserts into the index (one for each key extracted
+    from the indexed item).
+    <acronym>GIN</acronym> is capable of postponing much of this work by inserting
+    new tuples into a temporary, unsorted list of pending entries.
+    When the table is vacuumed or autoanalyzed, or when
+    <function>gin_clean_pending_list</function> function is called, or if the
+    pending list becomes larger than
+    <xref linkend="guc-gin-pending-list-limit"/>, the entries are moved to the
+    main <acronym>GIN</acronym> data structure using the same bulk insert
+    techniques used during initial index creation.  This greatly improves
+    <acronym>GIN</acronym> index update speed, even counting the additional
+    vacuum overhead.  Moreover the overhead work can be done by a background
+    process instead of in foreground query processing.
+   </para>
+
+   <para>
+    The main disadvantage of this approach is that searches must scan the list
+    of pending entries in addition to searching the regular index, and so
+    a large list of pending entries will slow searches significantly.
+    Another disadvantage is that, while most updates are fast, an update
+    that causes the pending list to become <quote>too large</quote> will incur an
+    immediate cleanup cycle and thus be much slower than other updates.
+    Proper use of autovacuum can minimize both of these problems.
+   </para>
+
+   <para>
+    If consistent response time is more important than update speed,
+    use of pending entries can be disabled by turning off the
+    <literal>fastupdate</literal> storage parameter for a
+    <acronym>GIN</acronym> index.  See <xref linkend="sql-createindex"/>
+    for details.
+   </para>
+  </sect3>
+
+  <sect3 id="gin-partial-match">
+   <title>Partial Match Algorithm</title>
+
+   <para>
+    GIN can support <quote>partial match</quote> queries, in which the query
+    does not determine an exact match for one or more keys, but the possible
+    matches fall within a reasonably narrow range of key values (within the
+    key sorting order determined by the <function>compare</function> support method).
+    The <function>extractQuery</function> method, instead of returning a key value
+    to be matched exactly, returns a key value that is the lower bound of
+    the range to be searched, and sets the <literal>pmatch</literal> flag true.
+    The key range is then scanned using the <function>comparePartial</function>
+    method.  <function>comparePartial</function> must return zero for a matching
+    index key, less than zero for a non-match that is still within the range
+    to be searched, or greater than zero if the index key is past the range
+    that could match.
+   </para>
+  </sect3>
+
+ </sect2>
+
+ <sect2 id="gin-tips">
+ <title>GIN Tips and Tricks</title>
+
+  <variablelist>
+   <varlistentry>
+    <term>Create vs. insert</term>
+    <listitem>
+     <para>
+      Insertion into a <acronym>GIN</acronym> index can be slow
+      due to the likelihood of many keys being inserted for each item.
+      So, for bulk insertions into a table it is advisable to drop the GIN
+      index and recreate it after finishing bulk insertion.
+     </para>
+
+     <para>
+      When <literal>fastupdate</literal> is enabled for <acronym>GIN</acronym>
+      (see <xref linkend="gin-fast-update"/> for details), the penalty is
+      less than when it is not.  But for very large updates it may still be
+      best to drop and recreate the index.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><xref linkend="guc-maintenance-work-mem"/></term>
+    <listitem>
+     <para>
+      Build time for a <acronym>GIN</acronym> index is very sensitive to
+      the <varname>maintenance_work_mem</varname> setting; it doesn't pay to
+      skimp on work memory during index creation.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><xref linkend="guc-gin-pending-list-limit"/></term>
+    <listitem>
+     <para>
+      During a series of insertions into an existing <acronym>GIN</acronym>
+      index that has <literal>fastupdate</literal> enabled, the system will clean up
+      the pending-entry list whenever the list grows larger than
+      <varname>gin_pending_list_limit</varname>. To avoid fluctuations in observed
+      response time, it's desirable to have pending-list cleanup occur in the
+      background (i.e., via autovacuum).  Foreground cleanup operations
+      can be avoided by increasing <varname>gin_pending_list_limit</varname>
+      or making autovacuum more aggressive.
+      However, enlarging the threshold of the cleanup operation means that
+      if a foreground cleanup does occur, it will take even longer.
+     </para>
+     <para>
+      <varname>gin_pending_list_limit</varname> can be overridden for individual
+      GIN indexes by changing storage parameters, which allows each
+      GIN index to have its own cleanup threshold.
+      For example, it's possible to increase the threshold only for the GIN
+      index which can be updated heavily, and decrease it otherwise.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><xref linkend="guc-gin-fuzzy-search-limit"/></term>
+    <listitem>
+     <para>
+      The primary goal of developing <acronym>GIN</acronym> indexes was
+      to create support for highly scalable full-text search in
+      <productname>PostgreSQL</productname>, and there are often situations when
+      a full-text search returns a very large set of results.  Moreover, this
+      often happens when the query contains very frequent words, so that the
+      large result set is not even useful.  Since reading many
+      tuples from the disk and sorting them could take a lot of time, this is
+      unacceptable for production.  (Note that the index search itself is very
+      fast.)
+     </para>
+     <para>
+      To facilitate controlled execution of such queries,
+      <acronym>GIN</acronym> has a configurable soft upper limit on the
+      number of rows returned: the
+      <varname>gin_fuzzy_search_limit</varname> configuration parameter.
+      It is set to 0 (meaning no limit) by default.
+      If a non-zero limit is set, then the returned set is a subset of
+      the whole result set, chosen at random.
+     </para>
+     <para>
+      <quote>Soft</quote> means that the actual number of returned results
+      could differ somewhat from the specified limit, depending on the query
+      and the quality of the system's random number generator.
+     </para>
+     <para>
+      From experience, values in the thousands (e.g., 5000 &mdash; 20000)
+      work well.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
+ </sect2>
+
+ <sect2 id="gin-limit">
+  <title>Limitations</title>
+
+  <para>
+   <acronym>GIN</acronym> assumes that indexable operators are strict.  This
+   means that <function>extractValue</function> will not be called at all on a null
+   item value (instead, a placeholder index entry is created automatically),
+   and <function>extractQuery</function> will not be called on a null query
+   value either (instead, the query is presumed to be unsatisfiable).  Note
+   however that null key values contained within a non-null composite item
+   or query value are supported.
+  </para>
+ </sect2>
+
+ <sect2 id="gin-examples">
+  <title>Examples</title>
+
+  <para>
+   The core <productname>PostgreSQL</productname> distribution
+   includes the <acronym>GIN</acronym> operator classes previously shown in
+   <xref linkend="gin-builtin-opclasses-table"/>.
+   The following <filename>contrib</filename> modules also contain
+   <acronym>GIN</acronym> operator classes:
+
+  <variablelist>
+   <varlistentry>
+    <term><filename>btree_gin</filename></term>
+    <listitem>
+     <para>B-tree equivalent functionality for several data types</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><filename>hstore</filename></term>
+    <listitem>
+     <para>Module for storing (key, value) pairs</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><filename>intarray</filename></term>
+    <listitem>
+     <para>Enhanced support for <type>int[]</type></para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><filename>pg_trgm</filename></term>
+    <listitem>
+     <para>Text similarity using trigram matching</para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+  </para>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="brin">
+ <title>BRIN Indexes</title>
+
+    <indexterm>
+     <primary>index</primary>
+     <secondary>BRIN</secondary>
+    </indexterm>
+
+ <sect2 id="brin-intro">
+  <title>Introduction</title>
+
+  <para>
+   <acronym>BRIN</acronym> stands for Block Range Index.
+   <acronym>BRIN</acronym> is designed for handling very large tables
+   in which certain columns have some natural correlation with their
+   physical location within the table.
+  </para>
+
+  <para>
+   <acronym>BRIN</acronym> works in terms of <firstterm>block ranges</firstterm>
+   (or <quote>page ranges</quote>).
+   A block range is a group of pages that are physically
+   adjacent in the table; for each block range, some summary info is stored
+   by the index.
+   For example, a table storing a store's sale orders might have
+   a date column on which each order was placed, and most of the time
+   the entries for earlier orders will appear earlier in the table as well;
+   a table storing a ZIP code column might have all codes for a city
+   grouped together naturally.
+  </para>
+
+  <para>
+   <acronym>BRIN</acronym> indexes can satisfy queries via regular bitmap
+   index scans, and will return all tuples in all pages within each range if
+   the summary info stored by the index is <firstterm>consistent</firstterm> with the
+   query conditions.
+   The query executor is in charge of rechecking these tuples and discarding
+   those that do not match the query conditions &mdash; in other words, these
+   indexes are lossy.
+   Because a <acronym>BRIN</acronym> index is very small, scanning the index
+   adds little overhead compared to a sequential scan, but may avoid scanning
+   large parts of the table that are known not to contain matching tuples.
+  </para>
+
+  <para>
+   The specific data that a <acronym>BRIN</acronym> index will store,
+   as well as the specific queries that the index will be able to satisfy,
+   depend on the operator class selected for each column of the index.
+   Data types having a linear sort order can have operator classes that
+   store the minimum and maximum value within each block range, for instance;
+   geometrical types might store the bounding box for all the objects
+   in the block range.
+  </para>
+
+  <para>
+   The size of the block range is determined at index creation time by
+   the <literal>pages_per_range</literal> storage parameter.  The number of index
+   entries will be equal to the size of the relation in pages divided by
+   the selected value for <literal>pages_per_range</literal>.  Therefore, the smaller
+   the number, the larger the index becomes (because of the need to
+   store more index entries), but at the same time the summary data stored can
+   be more precise and more data blocks can be skipped during an index scan.
+  </para>
+
+  <sect3 id="brin-operation">
+   <title>Index Maintenance</title>
+
+   <para>
+    At the time of creation, all existing heap pages are scanned and a
+    summary index tuple is created for each range, including the
+    possibly-incomplete range at the end.
+    As new pages are filled with data, page ranges that are already
+    summarized will cause the summary information to be updated with data
+    from the new tuples.
+    When a new page is created that does not fall within the last
+    summarized range, the range that the new page belongs to
+    does not automatically acquire a summary tuple;
+    those tuples remain unsummarized until a summarization run is
+    invoked later, creating the initial summary for that range.
+   </para>
+
+   <para>
+    There are several ways to trigger the initial summarization of a page range.
+    If the table is vacuumed, either manually or by
+    <link linkend="autovacuum">autovacuum</link>, all existing unsummarized
+    page ranges are summarized.
+    Also, if the index's
+    <xref linkend="index-reloption-autosummarize"/> parameter is enabled,
+    which it isn't by default,
+    whenever autovacuum runs in that database, summarization will occur for all
+    unsummarized page ranges that have been filled,
+    regardless of whether the table itself is processed by autovacuum; see below.
+   </para>
+
+   <para>
+    Lastly, the following functions can be used (while these functions run,
+    <xref linkend="guc-search-path"/> is temporarily changed to
+    <literal>pg_catalog, pg_temp</literal>):
+    <simplelist>
+     <member>
+      <function>brin_summarize_new_values(regclass)</function>
+      which summarizes all unsummarized ranges;
+     </member>
+     <member>
+      <function>brin_summarize_range(regclass, bigint)</function>
+      which summarizes only the range containing the given page,
+      if it is unsummarized.
+     </member>
+    </simplelist>
+   </para>
+
+   <para>
+    When autosummarization is enabled, a request is sent to
+    <literal>autovacuum</literal> to execute a targeted summarization
+    for a block range when an insertion is detected for the first item
+    of the first page of the next block range,
+    to be fulfilled the next time an autovacuum
+    worker finishes running in the
+    same database.  If the request queue is full, the request is not recorded
+    and a message is sent to the server log:
+ <screen>
+ LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was not recorded
+ </screen>
+    When this happens, the range will remain unsummarized until the next
+    regular vacuum run on the table, or one of the functions mentioned above
+    are invoked.
+   </para>
+
+   <para>
+    Conversely, a range can be de-summarized using the
+    <function>brin_desummarize_range(regclass, bigint)</function> function,
+    which is useful when the index tuple is no longer a very good
+    representation because the existing values have changed.
+    See <xref linkend="functions-admin-index"/> for details.
+   </para>
+
+  </sect3>
+ </sect2>
+
+ <sect2 id="brin-builtin-opclasses">
+  <title>Built-in Operator Classes</title>
+
+  <para>
+   The core <productname>PostgreSQL</productname> distribution
+   includes the <acronym>BRIN</acronym> operator classes shown in
+   <xref linkend="brin-builtin-opclasses-table"/>.
+  </para>
+
+  <para>
+   The <firstterm>minmax</firstterm>
+   operator classes store the minimum and the maximum values appearing
+   in the indexed column within the range.  The <firstterm>inclusion</firstterm>
+   operator classes store a value which includes the values in the indexed
+   column within the range.  The <firstterm>bloom</firstterm> operator
+   classes build a Bloom filter for all values in the range.  The
+   <firstterm>minmax-multi</firstterm> operator classes store multiple
+   minimum and maximum values, representing values appearing in the indexed
+   column within the range.
+  </para>
+
+  <table id="brin-builtin-opclasses-table">
+   <title>Built-in <acronym>BRIN</acronym> Operator Classes</title>
+   <tgroup cols="2">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Indexable Operators</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry valign="middle" morerows="4"><literal>bit_minmax_ops</literal></entry>
+      <entry><literal>= (bit,bit)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (bit,bit)</literal></entry></row>
+     <row><entry><literal>&gt; (bit,bit)</literal></entry></row>
+     <row><entry><literal>&lt;= (bit,bit)</literal></entry></row>
+     <row><entry><literal>&gt;= (bit,bit)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="12"><literal>box_inclusion_ops</literal></entry>
+      <entry><literal>@&gt; (box,point)</literal></entry>
+     </row>
+     <row><entry><literal>&lt;&lt; (box,box)</literal></entry></row>
+     <row><entry><literal>&amp;&lt; (box,box)</literal></entry></row>
+     <row><entry><literal>&amp;&gt; (box,box)</literal></entry></row>
+     <row><entry><literal>&gt;&gt; (box,box)</literal></entry></row>
+     <row><entry><literal>&lt;@ (box,box)</literal></entry></row>
+     <row><entry><literal>@&gt; (box,box)</literal></entry></row>
+     <row><entry><literal>~= (box,box)</literal></entry></row>
+     <row><entry><literal>&amp;&amp; (box,box)</literal></entry></row>
+     <row><entry><literal>&lt;&lt;| (box,box)</literal></entry></row>
+     <row><entry><literal>&amp;&lt;| (box,box)</literal></entry></row>
+     <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row>
+     <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>bpchar_bloom_ops</literal></entry>
+      <entry><literal>= (character,character)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>bpchar_minmax_ops</literal></entry>
+      <entry><literal>= (character,character)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (character,character)</literal></entry></row>
+     <row><entry><literal>&lt;= (character,character)</literal></entry></row>
+     <row><entry><literal>&gt; (character,character)</literal></entry></row>
+     <row><entry><literal>&gt;= (character,character)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>bytea_bloom_ops</literal></entry>
+      <entry><literal>= (bytea,bytea)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>bytea_minmax_ops</literal></entry>
+      <entry><literal>= (bytea,bytea)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (bytea,bytea)</literal></entry></row>
+     <row><entry><literal>&lt;= (bytea,bytea)</literal></entry></row>
+     <row><entry><literal>&gt; (bytea,bytea)</literal></entry></row>
+     <row><entry><literal>&gt;= (bytea,bytea)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>char_bloom_ops</literal></entry>
+      <entry><literal>= ("char","char")</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>char_minmax_ops</literal></entry>
+      <entry><literal>= ("char","char")</literal></entry>
+     </row>
+     <row><entry><literal>&lt; ("char","char")</literal></entry></row>
+     <row><entry><literal>&lt;= ("char","char")</literal></entry></row>
+     <row><entry><literal>&gt; ("char","char")</literal></entry></row>
+     <row><entry><literal>&gt;= ("char","char")</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>date_bloom_ops</literal></entry>
+      <entry><literal>= (date,date)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>date_minmax_ops</literal></entry>
+      <entry><literal>= (date,date)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (date,date)</literal></entry></row>
+     <row><entry><literal>&lt;= (date,date)</literal></entry></row>
+     <row><entry><literal>&gt; (date,date)</literal></entry></row>
+     <row><entry><literal>&gt;= (date,date)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>date_minmax_multi_ops</literal></entry>
+      <entry><literal>= (date,date)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (date,date)</literal></entry></row>
+     <row><entry><literal>&lt;= (date,date)</literal></entry></row>
+     <row><entry><literal>&gt; (date,date)</literal></entry></row>
+     <row><entry><literal>&gt;= (date,date)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>float4_bloom_ops</literal></entry>
+      <entry><literal>= (float4,float4)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>float4_minmax_ops</literal></entry>
+      <entry><literal>= (float4,float4)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (float4,float4)</literal></entry></row>
+     <row><entry><literal>&gt; (float4,float4)</literal></entry></row>
+     <row><entry><literal>&lt;= (float4,float4)</literal></entry></row>
+     <row><entry><literal>&gt;= (float4,float4)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>float4_minmax_multi_ops</literal></entry>
+      <entry><literal>= (float4,float4)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (float4,float4)</literal></entry></row>
+     <row><entry><literal>&gt; (float4,float4)</literal></entry></row>
+     <row><entry><literal>&lt;= (float4,float4)</literal></entry></row>
+     <row><entry><literal>&gt;= (float4,float4)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>float8_bloom_ops</literal></entry>
+      <entry><literal>= (float8,float8)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>float8_minmax_ops</literal></entry>
+      <entry><literal>= (float8,float8)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (float8,float8)</literal></entry></row>
+     <row><entry><literal>&lt;= (float8,float8)</literal></entry></row>
+     <row><entry><literal>&gt; (float8,float8)</literal></entry></row>
+     <row><entry><literal>&gt;= (float8,float8)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>float8_minmax_multi_ops</literal></entry>
+      <entry><literal>= (float8,float8)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (float8,float8)</literal></entry></row>
+     <row><entry><literal>&lt;= (float8,float8)</literal></entry></row>
+     <row><entry><literal>&gt; (float8,float8)</literal></entry></row>
+     <row><entry><literal>&gt;= (float8,float8)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="5"><literal>inet_inclusion_ops</literal></entry>
+      <entry><literal>&lt;&lt; (inet,inet)</literal></entry>
+     </row>
+     <row><entry><literal>&lt;&lt;= (inet,inet)</literal></entry></row>
+     <row><entry><literal>&gt;&gt; (inet,inet)</literal></entry></row>
+     <row><entry><literal>&gt;&gt;= (inet,inet)</literal></entry></row>
+     <row><entry><literal>= (inet,inet)</literal></entry></row>
+     <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>inet_bloom_ops</literal></entry>
+      <entry><literal>= (inet,inet)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>inet_minmax_ops</literal></entry>
+      <entry><literal>= (inet,inet)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (inet,inet)</literal></entry></row>
+     <row><entry><literal>&lt;= (inet,inet)</literal></entry></row>
+     <row><entry><literal>&gt; (inet,inet)</literal></entry></row>
+     <row><entry><literal>&gt;= (inet,inet)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>inet_minmax_multi_ops</literal></entry>
+      <entry><literal>= (inet,inet)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (inet,inet)</literal></entry></row>
+     <row><entry><literal>&lt;= (inet,inet)</literal></entry></row>
+     <row><entry><literal>&gt; (inet,inet)</literal></entry></row>
+     <row><entry><literal>&gt;= (inet,inet)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>int2_bloom_ops</literal></entry>
+      <entry><literal>= (int2,int2)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>int2_minmax_ops</literal></entry>
+      <entry><literal>= (int2,int2)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (int2,int2)</literal></entry></row>
+     <row><entry><literal>&gt; (int2,int2)</literal></entry></row>
+     <row><entry><literal>&lt;= (int2,int2)</literal></entry></row>
+     <row><entry><literal>&gt;= (int2,int2)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>int2_minmax_multi_ops</literal></entry>
+      <entry><literal>= (int2,int2)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (int2,int2)</literal></entry></row>
+     <row><entry><literal>&gt; (int2,int2)</literal></entry></row>
+     <row><entry><literal>&lt;= (int2,int2)</literal></entry></row>
+     <row><entry><literal>&gt;= (int2,int2)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>int4_bloom_ops</literal></entry>
+      <entry><literal>= (int4,int4)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>int4_minmax_ops</literal></entry>
+      <entry><literal>= (int4,int4)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (int4,int4)</literal></entry></row>
+     <row><entry><literal>&gt; (int4,int4)</literal></entry></row>
+     <row><entry><literal>&lt;= (int4,int4)</literal></entry></row>
+     <row><entry><literal>&gt;= (int4,int4)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>int4_minmax_multi_ops</literal></entry>
+      <entry><literal>= (int4,int4)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (int4,int4)</literal></entry></row>
+     <row><entry><literal>&gt; (int4,int4)</literal></entry></row>
+     <row><entry><literal>&lt;= (int4,int4)</literal></entry></row>
+     <row><entry><literal>&gt;= (int4,int4)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>int8_bloom_ops</literal></entry>
+      <entry><literal>= (bigint,bigint)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>int8_minmax_ops</literal></entry>
+      <entry><literal>= (bigint,bigint)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row>
+     <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row>
+     <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row>
+     <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>int8_minmax_multi_ops</literal></entry>
+      <entry><literal>= (bigint,bigint)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (bigint,bigint)</literal></entry></row>
+     <row><entry><literal>&gt; (bigint,bigint)</literal></entry></row>
+     <row><entry><literal>&lt;= (bigint,bigint)</literal></entry></row>
+     <row><entry><literal>&gt;= (bigint,bigint)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>interval_bloom_ops</literal></entry>
+      <entry><literal>= (interval,interval)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>interval_minmax_ops</literal></entry>
+      <entry><literal>= (interval,interval)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (interval,interval)</literal></entry></row>
+     <row><entry><literal>&lt;= (interval,interval)</literal></entry></row>
+     <row><entry><literal>&gt; (interval,interval)</literal></entry></row>
+     <row><entry><literal>&gt;= (interval,interval)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>interval_minmax_multi_ops</literal></entry>
+      <entry><literal>= (interval,interval)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (interval,interval)</literal></entry></row>
+     <row><entry><literal>&lt;= (interval,interval)</literal></entry></row>
+     <row><entry><literal>&gt; (interval,interval)</literal></entry></row>
+     <row><entry><literal>&gt;= (interval,interval)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>macaddr_bloom_ops</literal></entry>
+      <entry><literal>= (macaddr,macaddr)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>macaddr_minmax_ops</literal></entry>
+      <entry><literal>= (macaddr,macaddr)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row>
+     <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row>
+     <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row>
+     <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>macaddr_minmax_multi_ops</literal></entry>
+      <entry><literal>= (macaddr,macaddr)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (macaddr,macaddr)</literal></entry></row>
+     <row><entry><literal>&lt;= (macaddr,macaddr)</literal></entry></row>
+     <row><entry><literal>&gt; (macaddr,macaddr)</literal></entry></row>
+     <row><entry><literal>&gt;= (macaddr,macaddr)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>macaddr8_bloom_ops</literal></entry>
+      <entry><literal>= (macaddr8,macaddr8)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>macaddr8_minmax_ops</literal></entry>
+      <entry><literal>= (macaddr8,macaddr8)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row>
+     <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row>
+     <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row>
+     <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>macaddr8_minmax_multi_ops</literal></entry>
+      <entry><literal>= (macaddr8,macaddr8)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (macaddr8,macaddr8)</literal></entry></row>
+     <row><entry><literal>&lt;= (macaddr8,macaddr8)</literal></entry></row>
+     <row><entry><literal>&gt; (macaddr8,macaddr8)</literal></entry></row>
+     <row><entry><literal>&gt;= (macaddr8,macaddr8)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>name_bloom_ops</literal></entry>
+      <entry><literal>= (name,name)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>name_minmax_ops</literal></entry>
+      <entry><literal>= (name,name)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (name,name)</literal></entry></row>
+     <row><entry><literal>&lt;= (name,name)</literal></entry></row>
+     <row><entry><literal>&gt; (name,name)</literal></entry></row>
+     <row><entry><literal>&gt;= (name,name)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>numeric_bloom_ops</literal></entry>
+      <entry><literal>= (numeric,numeric)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>numeric_minmax_ops</literal></entry>
+      <entry><literal>= (numeric,numeric)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row>
+     <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row>
+     <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row>
+     <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>numeric_minmax_multi_ops</literal></entry>
+      <entry><literal>= (numeric,numeric)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (numeric,numeric)</literal></entry></row>
+     <row><entry><literal>&lt;= (numeric,numeric)</literal></entry></row>
+     <row><entry><literal>&gt; (numeric,numeric)</literal></entry></row>
+     <row><entry><literal>&gt;= (numeric,numeric)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>oid_bloom_ops</literal></entry>
+      <entry><literal>= (oid,oid)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>oid_minmax_ops</literal></entry>
+      <entry><literal>= (oid,oid)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (oid,oid)</literal></entry></row>
+     <row><entry><literal>&gt; (oid,oid)</literal></entry></row>
+     <row><entry><literal>&lt;= (oid,oid)</literal></entry></row>
+     <row><entry><literal>&gt;= (oid,oid)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>oid_minmax_multi_ops</literal></entry>
+      <entry><literal>= (oid,oid)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (oid,oid)</literal></entry></row>
+     <row><entry><literal>&gt; (oid,oid)</literal></entry></row>
+     <row><entry><literal>&lt;= (oid,oid)</literal></entry></row>
+     <row><entry><literal>&gt;= (oid,oid)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>pg_lsn_bloom_ops</literal></entry>
+      <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_ops</literal></entry>
+      <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row>
+     <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row>
+     <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row>
+     <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>pg_lsn_minmax_multi_ops</literal></entry>
+      <entry><literal>= (pg_lsn,pg_lsn)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (pg_lsn,pg_lsn)</literal></entry></row>
+     <row><entry><literal>&gt; (pg_lsn,pg_lsn)</literal></entry></row>
+     <row><entry><literal>&lt;= (pg_lsn,pg_lsn)</literal></entry></row>
+     <row><entry><literal>&gt;= (pg_lsn,pg_lsn)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="13"><literal>range_inclusion_ops</literal></entry>
+      <entry><literal>= (anyrange,anyrange)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&lt;= (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&gt;= (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&gt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&amp;&amp; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>@&gt; (anyrange,anyelement)</literal></entry></row>
+     <row><entry><literal>@&gt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&lt;@ (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&lt;&lt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&gt;&gt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&amp;&lt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>text_bloom_ops</literal></entry>
+      <entry><literal>= (text,text)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>text_minmax_ops</literal></entry>
+      <entry><literal>= (text,text)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (text,text)</literal></entry></row>
+     <row><entry><literal>&lt;= (text,text)</literal></entry></row>
+     <row><entry><literal>&gt; (text,text)</literal></entry></row>
+     <row><entry><literal>&gt;= (text,text)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>tid_bloom_ops</literal></entry>
+      <entry><literal>= (tid,tid)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>tid_minmax_ops</literal></entry>
+      <entry><literal>= (tid,tid)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (tid,tid)</literal></entry></row>
+     <row><entry><literal>&gt; (tid,tid)</literal></entry></row>
+     <row><entry><literal>&lt;= (tid,tid)</literal></entry></row>
+     <row><entry><literal>&gt;= (tid,tid)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>tid_minmax_multi_ops</literal></entry>
+      <entry><literal>= (tid,tid)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (tid,tid)</literal></entry></row>
+     <row><entry><literal>&gt; (tid,tid)</literal></entry></row>
+     <row><entry><literal>&lt;= (tid,tid)</literal></entry></row>
+     <row><entry><literal>&gt;= (tid,tid)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>timestamp_bloom_ops</literal></entry>
+      <entry><literal>= (timestamp,timestamp)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>timestamp_minmax_ops</literal></entry>
+      <entry><literal>= (timestamp,timestamp)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row>
+     <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row>
+     <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row>
+     <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>timestamp_minmax_multi_ops</literal></entry>
+      <entry><literal>= (timestamp,timestamp)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (timestamp,timestamp)</literal></entry></row>
+     <row><entry><literal>&lt;= (timestamp,timestamp)</literal></entry></row>
+     <row><entry><literal>&gt; (timestamp,timestamp)</literal></entry></row>
+     <row><entry><literal>&gt;= (timestamp,timestamp)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>timestamptz_bloom_ops</literal></entry>
+      <entry><literal>= (timestamptz,timestamptz)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>timestamptz_minmax_ops</literal></entry>
+      <entry><literal>= (timestamptz,timestamptz)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row>
+     <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row>
+     <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row>
+     <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>timestamptz_minmax_multi_ops</literal></entry>
+      <entry><literal>= (timestamptz,timestamptz)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (timestamptz,timestamptz)</literal></entry></row>
+     <row><entry><literal>&lt;= (timestamptz,timestamptz)</literal></entry></row>
+     <row><entry><literal>&gt; (timestamptz,timestamptz)</literal></entry></row>
+     <row><entry><literal>&gt;= (timestamptz,timestamptz)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>time_bloom_ops</literal></entry>
+      <entry><literal>= (time,time)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>time_minmax_ops</literal></entry>
+      <entry><literal>= (time,time)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (time,time)</literal></entry></row>
+     <row><entry><literal>&lt;= (time,time)</literal></entry></row>
+     <row><entry><literal>&gt; (time,time)</literal></entry></row>
+     <row><entry><literal>&gt;= (time,time)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>time_minmax_multi_ops</literal></entry>
+      <entry><literal>= (time,time)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (time,time)</literal></entry></row>
+     <row><entry><literal>&lt;= (time,time)</literal></entry></row>
+     <row><entry><literal>&gt; (time,time)</literal></entry></row>
+     <row><entry><literal>&gt;= (time,time)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>timetz_bloom_ops</literal></entry>
+      <entry><literal>= (timetz,timetz)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>timetz_minmax_ops</literal></entry>
+      <entry><literal>= (timetz,timetz)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row>
+     <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row>
+     <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row>
+     <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>timetz_minmax_multi_ops</literal></entry>
+      <entry><literal>= (timetz,timetz)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (timetz,timetz)</literal></entry></row>
+     <row><entry><literal>&lt;= (timetz,timetz)</literal></entry></row>
+     <row><entry><literal>&gt; (timetz,timetz)</literal></entry></row>
+     <row><entry><literal>&gt;= (timetz,timetz)</literal></entry></row>
+
+     <row>
+      <entry valign="middle"><literal>uuid_bloom_ops</literal></entry>
+      <entry><literal>= (uuid,uuid)</literal></entry>
+     </row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>uuid_minmax_ops</literal></entry>
+      <entry><literal>= (uuid,uuid)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row>
+     <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row>
+     <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row>
+     <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>uuid_minmax_multi_ops</literal></entry>
+      <entry><literal>= (uuid,uuid)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (uuid,uuid)</literal></entry></row>
+     <row><entry><literal>&gt; (uuid,uuid)</literal></entry></row>
+     <row><entry><literal>&lt;= (uuid,uuid)</literal></entry></row>
+     <row><entry><literal>&gt;= (uuid,uuid)</literal></entry></row>
+
+     <row>
+      <entry valign="middle" morerows="4"><literal>varbit_minmax_ops</literal></entry>
+      <entry><literal>= (varbit,varbit)</literal></entry>
+     </row>
+     <row><entry><literal>&lt; (varbit,varbit)</literal></entry></row>
+     <row><entry><literal>&gt; (varbit,varbit)</literal></entry></row>
+     <row><entry><literal>&lt;= (varbit,varbit)</literal></entry></row>
+     <row><entry><literal>&gt;= (varbit,varbit)</literal></entry></row>
+    </tbody>
+   </tgroup>
+  </table>
+
+   <sect3 id="brin-builtin-opclasses--parameters">
+    <title>Operator Class Parameters</title>
+
+    <para>
+     Some of the built-in operator classes allow specifying parameters affecting
+     behavior of the operator class.  Each operator class has its own set of
+     allowed parameters.  Only the <literal>bloom</literal> and <literal>minmax-multi</literal>
+     operator classes allow specifying parameters:
+    </para>
+
+    <para>
+     bloom operator classes accept these parameters:
+    </para>
+
+    <variablelist>
+    <varlistentry>
+     <term><literal>n_distinct_per_range</literal></term>
+     <listitem>
+     <para>
+      Defines the estimated number of distinct non-null values in the block
+      range, used by <acronym>BRIN</acronym> bloom indexes for sizing of the
+      Bloom filter. It behaves similarly to <literal>n_distinct</literal> option
+      for <xref linkend="sql-altertable"/>. When set to a positive value,
+      each block range is assumed to contain this number of distinct non-null
+      values. When set to a negative value, which must be greater than or
+      equal to -1, the number of distinct non-null values is assumed to grow linearly with
+      the maximum possible number of tuples in the block range (about 290
+      rows per block). The default value is <literal>-0.1</literal>, and
+      the minimum number of distinct non-null values is <literal>16</literal>.
+     </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>false_positive_rate</literal></term>
+     <listitem>
+     <para>
+      Defines the desired false positive rate used by <acronym>BRIN</acronym>
+      bloom indexes for sizing of the Bloom filter. The values must be
+      between 0.0001 and 0.25. The default value is 0.01, which is 1% false
+      positive rate.
+     </para>
+     </listitem>
+    </varlistentry>
+
+    </variablelist>
+
+    <para>
+     minmax-multi operator classes accept these parameters:
+    </para>
+
+    <variablelist>
+    <varlistentry>
+     <term><literal>values_per_range</literal></term>
+     <listitem>
+     <para>
+      Defines the maximum number of values stored by <acronym>BRIN</acronym>
+      minmax indexes to summarize a block range. Each value may represent
+      either a point, or a boundary of an interval. Values must be between
+      8 and 256, and the default value is 32.
+     </para>
+     </listitem>
+    </varlistentry>
+
+    </variablelist>
+   </sect3>
+
+ </sect2>
+
+ <sect2 id="brin-extensibility">
+  <title>Extensibility</title>
+
+  <para>
+   The <acronym>BRIN</acronym> interface has a high level of abstraction,
+   requiring the access method implementer only to implement the semantics
+   of the data type being accessed.  The <acronym>BRIN</acronym> layer
+   itself takes care of concurrency, logging and searching the index structure.
+  </para>
+
+  <para>
+   All it takes to get a <acronym>BRIN</acronym> access method working is to
+   implement a few user-defined methods, which define the behavior of
+   summary values stored in the index and the way they interact with
+   scan keys.
+   In short, <acronym>BRIN</acronym> combines
+   extensibility with generality, code reuse, and a clean interface.
+  </para>
+
+  <para>
+   There are four methods that an operator class for <acronym>BRIN</acronym>
+   must provide:
+
+   <variablelist>
+    <varlistentry>
+     <term><function>BrinOpcInfo *opcInfo(Oid type_oid)</function></term>
+     <listitem>
+      <para>
+       Returns internal information about the indexed columns' summary data.
+       The return value must point to a palloc'd <structname>BrinOpcInfo</structname>,
+       which has this definition:
+<programlisting>
+typedef struct BrinOpcInfo
+{
+    /* Number of columns stored in an index column of this opclass */
+    uint16      oi_nstored;
+
+    /* Opaque pointer for the opclass' private use */
+    void       *oi_opaque;
+
+    /* Type cache entries of the stored columns */
+    TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER];
+} BrinOpcInfo;
+</programlisting>
+       <structname>BrinOpcInfo</structname>.<structfield>oi_opaque</structfield> can be used by the
+       operator class routines to pass information between support functions
+       during an index scan.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><function>bool consistent(BrinDesc *bdesc, BrinValues *column,
+        ScanKey *keys, int nkeys)</function></term>
+     <listitem>
+      <para>
+       Returns whether all the ScanKey entries are consistent with the given
+       indexed values for a range.
+       The attribute number to use is passed as part of the scan key.
+       Multiple scan keys for the same attribute may be passed at once; the
+       number of entries is determined by the <literal>nkeys</literal> parameter.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><function>bool consistent(BrinDesc *bdesc, BrinValues *column,
+        ScanKey key)</function></term>
+     <listitem>
+      <para>
+       Returns whether the ScanKey is consistent with the given indexed
+       values for a range.
+       The attribute number to use is passed as part of the scan key.
+       This is an older backward-compatible variant of the consistent function.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><function>bool addValue(BrinDesc *bdesc, BrinValues *column,
+        Datum newval, bool isnull)</function></term>
+     <listitem>
+      <para>
+       Given an index tuple and an indexed value, modifies the indicated
+       attribute of the tuple so that it additionally represents the new value.
+       If any modification was done to the tuple, <literal>true</literal> is
+       returned.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><function>bool unionTuples(BrinDesc *bdesc, BrinValues *a,
+        BrinValues *b)</function></term>
+     <listitem>
+      <para>
+       Consolidates two index tuples. Given two index tuples, modifies the
+       indicated attribute of the first of them so that it represents both tuples.
+       The second tuple is not modified.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+
+   An operator class for <acronym>BRIN</acronym> can optionally specify the
+   following method:
+
+   <variablelist>
+     <varlistentry>
+      <term><function>void options(local_relopts *relopts)</function></term>
+      <listitem>
+       <para>
+        Defines a set of user-visible parameters that control operator class
+        behavior.
+       </para>
+
+       <para>
+        The <function>options</function> function is passed a pointer to a
+        <structname>local_relopts</structname> struct, which needs to be
+        filled with a set of operator class specific options.  The options
+        can be accessed from other support functions using the
+        <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
+        <literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
+       </para>
+
+       <para>
+        Since both key extraction of indexed values and representation of the
+        key in <acronym>BRIN</acronym> are flexible, they may depend on
+        user-specified parameters.
+       </para>
+      </listitem>
+     </varlistentry>
+   </variablelist>
+
+   The core distribution includes support for four types of operator classes:
+   minmax, minmax-multi, inclusion and bloom.  Operator class definitions
+   using them are shipped for in-core data types as appropriate.  Additional
+   operator classes can be defined by the user for other data types using
+   equivalent definitions, without having to write any source code;
+   appropriate catalog entries being declared is enough.  Note that
+   assumptions about the semantics of operator strategies are embedded in the
+   support functions' source code.
+  </para>
+
+  <para>
+   Operator classes that implement completely different semantics are also
+   possible, provided implementations of the four main support functions
+   described above are written.  Note that backwards compatibility across major
+   releases is not guaranteed: for example, additional support functions might
+   be required in later releases.
+  </para>
+
+  <para>
+   To write an operator class for a data type that implements a totally
+   ordered set, it is possible to use the minmax support functions
+   alongside the corresponding operators, as shown in
+   <xref linkend="brin-extensibility-minmax-table"/>.
+   All operator class members (functions and operators) are mandatory.
+  </para>
+
+  <table id="brin-extensibility-minmax-table">
+   <title>Function and Support Numbers for Minmax Operator Classes</title>
+   <tgroup cols="2">
+    <colspec colname="col1" colwidth="1*"/>
+    <colspec colname="col2" colwidth="2*"/>
+    <thead>
+     <row>
+      <entry>Operator class member</entry>
+      <entry>Object</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry>Support Function 1</entry>
+      <entry>internal function <function>brin_minmax_opcinfo()</function></entry>
+     </row>
+     <row>
+      <entry>Support Function 2</entry>
+      <entry>internal function <function>brin_minmax_add_value()</function></entry>
+     </row>
+     <row>
+      <entry>Support Function 3</entry>
+      <entry>internal function <function>brin_minmax_consistent()</function></entry>
+     </row>
+     <row>
+      <entry>Support Function 4</entry>
+      <entry>internal function <function>brin_minmax_union()</function></entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 1</entry>
+      <entry>operator less-than</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 2</entry>
+      <entry>operator less-than-or-equal-to</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 3</entry>
+      <entry>operator equal-to</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 4</entry>
+      <entry>operator greater-than-or-equal-to</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 5</entry>
+      <entry>operator greater-than</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   To write an operator class for a complex data type which has values
+   included within another type, it's possible to use the inclusion support
+   functions alongside the corresponding operators, as shown
+   in <xref linkend="brin-extensibility-inclusion-table"/>.  It requires
+   only a single additional function, which can be written in any language.
+   More functions can be defined for additional functionality.  All operators
+   are optional.  Some operators require other operators, as shown as
+   dependencies on the table.
+  </para>
+
+  <table id="brin-extensibility-inclusion-table">
+   <title>Function and Support Numbers for Inclusion Operator Classes</title>
+   <tgroup cols="3">
+    <colspec colname="col1" colwidth="1*"/>
+    <colspec colname="col2" colwidth="2*"/>
+    <colspec colname="col3" colwidth="1*"/>
+    <thead>
+     <row>
+      <entry>Operator class member</entry>
+      <entry>Object</entry>
+      <entry>Dependency</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry>Support Function 1</entry>
+      <entry>internal function <function>brin_inclusion_opcinfo()</function></entry>
+      <entry></entry>
+     </row>
+     <row>
+      <entry>Support Function 2</entry>
+      <entry>internal function <function>brin_inclusion_add_value()</function></entry>
+      <entry></entry>
+     </row>
+     <row>
+      <entry>Support Function 3</entry>
+      <entry>internal function <function>brin_inclusion_consistent()</function></entry>
+      <entry></entry>
+     </row>
+     <row>
+      <entry>Support Function 4</entry>
+      <entry>internal function <function>brin_inclusion_union()</function></entry>
+      <entry></entry>
+     </row>
+     <row>
+      <entry>Support Function 11</entry>
+      <entry>function to merge two elements</entry>
+      <entry></entry>
+     </row>
+     <row>
+      <entry>Support Function 12</entry>
+      <entry>optional function to check whether two elements are mergeable</entry>
+      <entry></entry>
+     </row>
+     <row>
+      <entry>Support Function 13</entry>
+      <entry>optional function to check if an element is contained within another</entry>
+      <entry></entry>
+     </row>
+     <row>
+      <entry>Support Function 14</entry>
+      <entry>optional function to check whether an element is empty</entry>
+      <entry></entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 1</entry>
+      <entry>operator left-of</entry>
+      <entry>Operator Strategy 4</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 2</entry>
+      <entry>operator does-not-extend-to-the-right-of</entry>
+      <entry>Operator Strategy 5</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 3</entry>
+      <entry>operator overlaps</entry>
+      <entry></entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 4</entry>
+      <entry>operator does-not-extend-to-the-left-of</entry>
+      <entry>Operator Strategy 1</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 5</entry>
+      <entry>operator right-of</entry>
+      <entry>Operator Strategy 2</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 6, 18</entry>
+      <entry>operator same-as-or-equal-to</entry>
+      <entry>Operator Strategy 7</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 7, 16, 24, 25</entry>
+      <entry>operator contains-or-equal-to</entry>
+      <entry></entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 8, 26, 27</entry>
+      <entry>operator is-contained-by-or-equal-to</entry>
+      <entry>Operator Strategy 3</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 9</entry>
+      <entry>operator does-not-extend-above</entry>
+      <entry>Operator Strategy 11</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 10</entry>
+      <entry>operator is-below</entry>
+      <entry>Operator Strategy 12</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 11</entry>
+      <entry>operator is-above</entry>
+      <entry>Operator Strategy 9</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 12</entry>
+      <entry>operator does-not-extend-below</entry>
+      <entry>Operator Strategy 10</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 20</entry>
+      <entry>operator less-than</entry>
+      <entry>Operator Strategy 5</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 21</entry>
+      <entry>operator less-than-or-equal-to</entry>
+      <entry>Operator Strategy 5</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 22</entry>
+      <entry>operator greater-than</entry>
+      <entry>Operator Strategy 1</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 23</entry>
+      <entry>operator greater-than-or-equal-to</entry>
+      <entry>Operator Strategy 1</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+     Support function numbers 1 through 10 are reserved for the BRIN internal
+     functions, so the SQL level functions start with number 11.  Support
+     function number 11 is the main function required to build the index.
+     It should accept two arguments with the same data type as the operator class,
+     and return the union of them.  The inclusion operator class can store union
+     values with different data types if it is defined with the
+     <literal>STORAGE</literal> parameter.  The return value of the union
+     function should match the <literal>STORAGE</literal> data type.
+  </para>
+
+  <para>
+     Support function numbers 12 and 14 are provided to support
+     irregularities of built-in data types.  Function number 12
+     is used to support network addresses from different families which
+     are not mergeable.  Function number 14 is used to support
+     empty ranges.  Function number 13 is an optional but
+     recommended one, which allows the new value to be checked before
+     it is passed to the union function.  As the BRIN framework can shortcut
+     some operations when the union is not changed, using this
+     function can improve index performance.
+  </para>
+
+  <para>
+   To write an operator class for a data type that implements only an equality
+   operator and supports hashing, it is possible to use the bloom support procedures
+   alongside the corresponding operators, as shown in
+   <xref linkend="brin-extensibility-bloom-table"/>.
+   All operator class members (procedures and operators) are mandatory.
+  </para>
+
+  <table id="brin-extensibility-bloom-table">
+   <title>Procedure and Support Numbers for Bloom Operator Classes</title>
+   <tgroup cols="2">
+    <thead>
+     <row>
+      <entry>Operator class member</entry>
+      <entry>Object</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry>Support Procedure 1</entry>
+      <entry>internal function <function>brin_bloom_opcinfo()</function></entry>
+     </row>
+     <row>
+      <entry>Support Procedure 2</entry>
+      <entry>internal function <function>brin_bloom_add_value()</function></entry>
+     </row>
+     <row>
+      <entry>Support Procedure 3</entry>
+      <entry>internal function <function>brin_bloom_consistent()</function></entry>
+     </row>
+     <row>
+      <entry>Support Procedure 4</entry>
+      <entry>internal function <function>brin_bloom_union()</function></entry>
+     </row>
+     <row>
+      <entry>Support Procedure 5</entry>
+      <entry>internal function <function>brin_bloom_options()</function></entry>
+     </row>
+     <row>
+      <entry>Support Procedure 11</entry>
+      <entry>function to compute hash of an element</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 1</entry>
+      <entry>operator equal-to</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+     Support procedure numbers 1-10 are reserved for the BRIN internal
+     functions, so the SQL level functions start with number 11.  Support
+     function number 11 is the main function required to build the index.
+     It should accept one argument with the same data type as the operator class,
+     and return a hash of the value.
+  </para>
+
+  <para>
+   The minmax-multi operator class is also intended for data types implementing
+   a totally ordered set, and may be seen as a simple extension of the minmax
+   operator class. While minmax operator class summarizes values from each block
+   range into a single contiguous interval, minmax-multi allows summarization
+   into multiple smaller intervals to improve handling of outlier values.
+   It is possible to use the minmax-multi support procedures alongside the
+   corresponding operators, as shown in
+   <xref linkend="brin-extensibility-minmax-multi-table"/>.
+   All operator class members (procedures and operators) are mandatory.
+  </para>
+
+  <table id="brin-extensibility-minmax-multi-table">
+   <title>Procedure and Support Numbers for minmax-multi Operator Classes</title>
+   <tgroup cols="2">
+    <thead>
+     <row>
+      <entry>Operator class member</entry>
+      <entry>Object</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry>Support Procedure 1</entry>
+      <entry>internal function <function>brin_minmax_multi_opcinfo()</function></entry>
+     </row>
+     <row>
+      <entry>Support Procedure 2</entry>
+      <entry>internal function <function>brin_minmax_multi_add_value()</function></entry>
+     </row>
+     <row>
+      <entry>Support Procedure 3</entry>
+      <entry>internal function <function>brin_minmax_multi_consistent()</function></entry>
+     </row>
+     <row>
+      <entry>Support Procedure 4</entry>
+      <entry>internal function <function>brin_minmax_multi_union()</function></entry>
+     </row>
+     <row>
+      <entry>Support Procedure 5</entry>
+      <entry>internal function <function>brin_minmax_multi_options()</function></entry>
+     </row>
+     <row>
+      <entry>Support Procedure 11</entry>
+      <entry>function to compute distance between two values (length of a range)</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 1</entry>
+      <entry>operator less-than</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 2</entry>
+      <entry>operator less-than-or-equal-to</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 3</entry>
+      <entry>operator equal-to</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 4</entry>
+      <entry>operator greater-than-or-equal-to</entry>
+     </row>
+     <row>
+      <entry>Operator Strategy 5</entry>
+      <entry>operator greater-than</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+     Both minmax and inclusion operator classes support cross-data-type
+     operators, though with these the dependencies become more complicated.
+     The minmax operator class requires a full set of operators to be
+     defined with both arguments having the same data type.  It allows
+     additional data types to be supported by defining extra sets
+     of operators.  Inclusion operator class operator strategies are dependent
+     on another operator strategy as shown in
+     <xref linkend="brin-extensibility-inclusion-table"/>, or the same
+     operator strategy as themselves.  They require the dependency
+     operator to be defined with the <literal>STORAGE</literal> data type as the
+     left-hand-side argument and the other supported data type to be the
+     right-hand-side argument of the supported operator.  See
+     <literal>float4_minmax_ops</literal> as an example of minmax, and
+     <literal>box_inclusion_ops</literal> as an example of inclusion.
+  </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="hash-index">
+ <title>Hash Indexes</title>
+
+    <indexterm>
+     <primary>index</primary>
+     <secondary>Hash</secondary>
+    </indexterm>
+
+ <sect2 id="hash-intro">
+  <title>Overview</title>
+
+  <para>
+   <productname>PostgreSQL</productname>
+   includes an implementation of persistent on-disk hash indexes,
+   which are fully crash recoverable. Any data type can be indexed by a
+   hash index, including data types that do not have a well-defined linear
+   ordering. Hash indexes store only the hash value of the data being
+   indexed, thus there are no restrictions on the size of the data column
+   being indexed.
+  </para>
+
+  <para>
+   Hash indexes support only single-column indexes and do not allow
+   uniqueness checking.
+  </para>
+
+  <para>
+   Hash indexes support only the <literal>=</literal> operator,
+   so WHERE clauses that specify range operations will not be able to take
+   advantage of hash indexes.
+  </para>
+
+  <para>
+   Each hash index tuple stores just the 4-byte hash value, not the actual
+   column value. As a result, hash indexes may be much smaller than B-trees
+   when indexing longer data items such as UUIDs, URLs, etc. The absence of
+   the column value also makes all hash index scans lossy. Hash indexes may
+   take part in bitmap index scans and backward scans.
+  </para>
+
+  <para>
+   Hash indexes are best optimized for SELECT and UPDATE-heavy workloads
+   that use equality scans on larger tables. In a B-tree index, searches must
+   descend through the tree until the leaf page is found. In tables with
+   millions of rows, this descent can increase access time to data. The
+   equivalent of a leaf page in a hash index is referred to as a bucket page. In
+   contrast, a hash index allows accessing the bucket pages directly,
+   thereby potentially reducing index access time in larger tables. This
+   reduction in "logical I/O" becomes even more pronounced on indexes/data
+   larger than shared_buffers/RAM.
+  </para>
+
+  <para>
+   Hash indexes have been designed to cope with uneven distributions of
+   hash values. Direct access to the bucket pages works well if the hash
+   values are evenly distributed. When inserts mean that the bucket page
+   becomes full, additional overflow pages are chained to that specific
+   bucket page, locally expanding the storage for index tuples that match
+   that hash value. When scanning a hash bucket during queries, we need to
+   scan through all of the overflow pages. Thus an unbalanced hash index
+   might actually be worse than a B-tree in terms of number of block
+   accesses required, for some data.
+  </para>
+
+  <para>
+   As a result of the overflow cases, we can say that hash indexes are
+   most suitable for unique, nearly unique data or data with a low number
+   of rows per hash bucket.
+   One possible way to avoid problems is to exclude highly non-unique
+   values from the index using a partial index condition, but this may
+   not be suitable in many cases.
+  </para>
+
+  <para>
+   Like B-Trees, hash indexes perform simple index tuple deletion. This
+   is a deferred maintenance operation that deletes index tuples that are
+   known to be safe to delete (those whose item identifier's LP_DEAD bit
+   is already set). If an insert finds no space is available on a page we
+   try to avoid creating a new overflow page by attempting to remove dead
+   index tuples. Removal cannot occur if the page is pinned at that time.
+   Deletion of dead index pointers also occurs during VACUUM.
+  </para>
+
+  <para>
+   If it can, VACUUM will also try to squeeze the index tuples onto as
+   few overflow pages as possible, minimizing the overflow chain. If an
+   overflow page becomes empty, overflow pages can be recycled for reuse
+   in other buckets, though we never return them to the operating system.
+   There is currently no provision to shrink a hash index, other than by
+   rebuilding it with REINDEX.
+   There is no provision for reducing the number of buckets, either.
+  </para>
+
+  <para>
+   Hash indexes may expand the number of bucket pages as the number of
+   rows indexed grows. The hash key-to-bucket-number mapping is chosen so that
+   the index can be incrementally expanded. When a new bucket is to be added to
+   the index, exactly one existing bucket will need to be "split", with some of
+   its tuples being transferred to the new bucket according to the updated
+   key-to-bucket-number mapping.
+  </para>
+
+  <para>
+   The expansion occurs in the foreground, which could increase execution
+   time for user inserts. Thus, hash indexes may not be suitable for tables
+   with rapidly increasing number of rows.
+  </para>
+
+ </sect2>
+
+ <sect2 id="hash-implementation">
+  <title>Implementation</title>
+
+  <para>
+   There are four kinds of pages in a hash index: the meta page (page zero),
+   which contains statically allocated control information; primary bucket
+   pages; overflow pages; and bitmap pages, which keep track of overflow
+   pages that have been freed and are available for re-use. For addressing
+   purposes, bitmap pages are regarded as a subset of the overflow pages.
+  </para>
+
+  <para>
+   Both scanning the index and inserting tuples require locating the bucket
+   where a given tuple ought to be located. To do this, we need the bucket
+   count, highmask, and lowmask from the metapage; however, it's undesirable
+   for performance reasons to have to have to lock and pin the metapage for
+   every such operation. Instead, we retain a cached copy of the metapage
+   in each backend's relcache entry. This will produce the correct bucket
+   mapping as long as the target bucket hasn't been split since the last
+   cache refresh.
+  </para>
+
+  <para>
+   Primary bucket pages and overflow pages are allocated independently since
+   any given index might need more or fewer overflow pages relative to its
+   number of buckets. The hash code uses an interesting set of addressing
+   rules to support a variable number of overflow pages while not having to
+   move primary bucket pages around after they are created.
+  </para>
+
+  <para>
+   Each row in the table indexed is represented by a single index tuple in
+   the hash index. Hash index tuples are stored in bucket pages, and if
+   they exist, overflow pages. We speed up searches by keeping the index entries
+   in any one index page sorted by hash code, thus allowing binary search to be
+   used within an index page. Note however that there is *no* assumption about
+   the relative ordering of hash codes across different index pages of a bucket.
+  </para>
+
+  <para>
+   The bucket splitting algorithms to expand the hash index are too complex to
+   be worthy of mention here, though are described in more detail in
+   <filename>src/backend/access/hash/README</filename>.
+   The split algorithm is crash safe and can be restarted if not completed
+   successfully.
+  </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 73b497bcf8..0235c0e352 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -257,12 +257,7 @@ break is not needed in a wider output rendering.
   &indexam;
   &generic-wal;
   &custom-rmgr;
-  &btree;
-  &gist;
-  &spgist;
-  &gin;
-  &brin;
-  &hash;
+  &indextypes;
   &storage;
   &transaction;
   &bki;
diff --git a/doc/src/sgml/spgist.sgml b/doc/src/sgml/spgist.sgml
deleted file mode 100644
index 102f8627bd..0000000000
--- a/doc/src/sgml/spgist.sgml
+++ /dev/null
@@ -1,1076 +0,0 @@
-<!-- doc/src/sgml/spgist.sgml -->
-
-<chapter id="spgist">
-<title>SP-GiST Indexes</title>
-
-   <indexterm>
-    <primary>index</primary>
-    <secondary>SP-GiST</secondary>
-   </indexterm>
-
-<sect1 id="spgist-intro">
- <title>Introduction</title>
-
- <para>
-  <acronym>SP-GiST</acronym> is an abbreviation for space-partitioned
-  <acronym>GiST</acronym>.  <acronym>SP-GiST</acronym> supports partitioned
-  search trees, which facilitate development of a wide range of different
-  non-balanced data structures, such as quad-trees, k-d trees, and radix
-  trees (tries).  The common feature of these structures is that they
-  repeatedly divide the search space into partitions that need not be
-  of equal size.  Searches that are well matched to the partitioning rule
-  can be very fast.
- </para>
-
- <para>
-  These popular data structures were originally developed for in-memory
-  usage.  In main memory, they are usually designed as a set of dynamically
-  allocated nodes linked by pointers.  This is not suitable for direct
-  storing on disk, since these chains of pointers can be rather long which
-  would require too many disk accesses.  In contrast, disk-based data
-  structures should have a high fanout to minimize I/O.  The challenge
-  addressed by <acronym>SP-GiST</acronym> is to map search tree nodes to
-  disk pages in such a way that a search need access only a few disk pages,
-  even if it traverses many nodes.
- </para>
-
- <para>
-  Like <acronym>GiST</acronym>, <acronym>SP-GiST</acronym> is meant to allow
-  the development of custom data types with the appropriate access methods,
-  by an expert in the domain of the data type, rather than a database expert.
- </para>
-
- <para>
-  Some of the information here is derived from Purdue University's
-  SP-GiST Indexing Project
-  <ulink url="https://www.cs.purdue.edu/spgist/">web site</ulink>.
-  The <acronym>SP-GiST</acronym> implementation in
-  <productname>PostgreSQL</productname> is primarily maintained by Teodor
-  Sigaev and Oleg Bartunov, and there is more information on their
-  <!-- URL will be changed -->
-  <ulink url="http://www.sai.msu.su/~megera/wiki/spgist_dev">web site</ulink>.
- </para>
-
-</sect1>
-
-<sect1 id="spgist-builtin-opclasses">
- <title>Built-in Operator Classes</title>
-
- <para>
-  The core <productname>PostgreSQL</productname> distribution
-  includes the <acronym>SP-GiST</acronym> operator classes shown in
-  <xref linkend="spgist-builtin-opclasses-table"/>.
- </para>
-
-  <table id="spgist-builtin-opclasses-table">
-   <title>Built-in <acronym>SP-GiST</acronym> Operator Classes</title>
-   <tgroup cols="3">
-    <thead>
-     <row>
-      <entry>Name</entry>
-      <entry>Indexable Operators</entry>
-      <entry>Ordering Operators</entry>
-     </row>
-    </thead>
-    <tbody>
-     <row>
-      <entry valign="middle" morerows="11"><literal>box_ops</literal></entry>
-      <entry><literal>&lt;&lt; (box,box)</literal></entry>
-      <entry valign="middle" morerows="11"><literal>&lt;-&gt; (box,point)</literal></entry>
-     </row>
-     <row><entry><literal>&amp;&lt; (box,box)</literal></entry></row>
-     <row><entry><literal>&amp;&gt; (box,box)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (box,box)</literal></entry></row>
-     <row><entry><literal>&lt;@ (box,box)</literal></entry></row>
-     <row><entry><literal>@&gt; (box,box)</literal></entry></row>
-     <row><entry><literal>~= (box,box)</literal></entry></row>
-     <row><entry><literal>&amp;&amp; (box,box)</literal></entry></row>
-     <row><entry><literal>&lt;&lt;| (box,box)</literal></entry></row>
-     <row><entry><literal>&amp;&lt;| (box,box)</literal></entry></row>
-     <row><entry><literal>|&amp;&gt; (box,box)</literal></entry></row>
-     <row><entry><literal>|&gt;&gt; (box,box)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="10"><literal>inet_ops</literal></entry>
-      <entry><literal>&lt;&lt; (inet,inet)</literal></entry>
-      <entry valign="middle" morerows="10"></entry>
-     </row>
-     <row><entry><literal>&lt;&lt;= (inet,inet)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (inet,inet)</literal></entry></row>
-     <row><entry><literal>&gt;&gt;= (inet,inet)</literal></entry></row>
-     <row><entry><literal>= (inet,inet)</literal></entry></row>
-     <row><entry><literal>&lt;&gt; (inet,inet)</literal></entry></row>
-     <row><entry><literal>&lt; (inet,inet)</literal></entry></row>
-     <row><entry><literal>&lt;= (inet,inet)</literal></entry></row>
-     <row><entry><literal>&gt; (inet,inet)</literal></entry></row>
-     <row><entry><literal>&gt;= (inet,inet)</literal></entry></row>
-     <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="5"><literal>kd_point_ops</literal></entry>
-      <entry><literal>|&gt;&gt; (point,point)</literal></entry>
-      <entry valign="middle" morerows="5"><literal>&lt;-&gt; (point,point)</literal></entry>
-     </row>
-     <row><entry><literal>&lt;&lt; (point,point)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (point,point)</literal></entry></row>
-     <row><entry><literal>&lt;&lt;| (point,point)</literal></entry></row>
-     <row><entry><literal>~= (point,point)</literal></entry></row>
-     <row><entry><literal>&lt;@ (point,box)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="11"><literal>poly_ops</literal></entry>
-      <entry><literal>&lt;&lt; (polygon,polygon)</literal></entry>
-      <entry valign="middle" morerows="11"><literal>&lt;-&gt; (polygon,point)</literal></entry>
-     </row>
-     <row><entry><literal>&amp;&lt; (polygon,polygon)</literal></entry></row>
-     <row><entry><literal>&amp;&gt; (polygon,polygon)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (polygon,polygon)</literal></entry></row>
-     <row><entry><literal>&lt;@ (polygon,polygon)</literal></entry></row>
-     <row><entry><literal>@&gt; (polygon,polygon)</literal></entry></row>
-     <row><entry><literal>~= (polygon,polygon)</literal></entry></row>
-     <row><entry><literal>&amp;&amp; (polygon,polygon)</literal></entry></row>
-     <row><entry><literal>&lt;&lt;| (polygon,polygon)</literal></entry></row>
-     <row><entry><literal>&amp;&lt;| (polygon,polygon)</literal></entry></row>
-     <row><entry><literal>|&gt;&gt; (polygon,polygon)</literal></entry></row>
-     <row><entry><literal>|&amp;&gt; (polygon,polygon)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="5"><literal>quad_point_ops</literal></entry>
-      <entry><literal>|&gt;&gt; (point,point)</literal></entry>
-      <entry valign="middle" morerows="5"><literal>&lt;-&gt; (point,point)</literal></entry>
-     </row>
-     <row><entry><literal>&lt;&lt; (point,point)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (point,point)</literal></entry></row>
-     <row><entry><literal>&lt;&lt;| (point,point)</literal></entry></row>
-     <row><entry><literal>~= (point,point)</literal></entry></row>
-     <row><entry><literal>&lt;@ (point,box)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="9"><literal>range_ops</literal></entry>
-      <entry><literal>= (anyrange,anyrange)</literal></entry>
-      <entry valign="middle" morerows="9"></entry>
-     </row>
-     <row><entry><literal>&amp;&amp; (anyrange,anyrange)</literal></entry></row>
-     <row><entry><literal>@&gt; (anyrange,anyelement)</literal></entry></row>
-     <row><entry><literal>@&gt; (anyrange,anyrange)</literal></entry></row>
-     <row><entry><literal>&lt;@ (anyrange,anyrange)</literal></entry></row>
-     <row><entry><literal>&lt;&lt; (anyrange,anyrange)</literal></entry></row>
-     <row><entry><literal>&gt;&gt; (anyrange,anyrange)</literal></entry></row>
-     <row><entry><literal>&amp;&lt; (anyrange,anyrange)</literal></entry></row>
-     <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row>
-     <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row>
-
-     <row>
-      <entry valign="middle" morerows="9"><literal>text_ops</literal></entry>
-      <entry><literal>= (text,text)</literal></entry>
-      <entry valign="middle" morerows="9"></entry>
-     </row>
-     <row><entry><literal>&lt; (text,text)</literal></entry></row>
-     <row><entry><literal>&lt;= (text,text)</literal></entry></row>
-     <row><entry><literal>&gt; (text,text)</literal></entry></row>
-     <row><entry><literal>&gt;= (text,text)</literal></entry></row>
-     <row><entry><literal>~&lt;~ (text,text)</literal></entry></row>
-     <row><entry><literal>~&lt;=~ (text,text)</literal></entry></row>
-     <row><entry><literal>~&gt;=~ (text,text)</literal></entry></row>
-     <row><entry><literal>~&gt;~ (text,text)</literal></entry></row>
-     <row><entry><literal>^@ (text,text)</literal></entry></row>
-    </tbody>
-   </tgroup>
-  </table>
-
- <para>
-  Of the two operator classes for type <type>point</type>,
-  <literal>quad_point_ops</literal> is the default.  <literal>kd_point_ops</literal>
-  supports the same operators but uses a different index data structure that
-  may offer better performance in some applications.
- </para>
- <para>
-  The <literal>quad_point_ops</literal>, <literal>kd_point_ops</literal> and
-  <literal>poly_ops</literal> operator classes support the <literal>&lt;-&gt;</literal>
-  ordering operator, which enables the k-nearest neighbor (<literal>k-NN</literal>)
-  search over indexed point or polygon data sets.
- </para>
-
-</sect1>
-
-<sect1 id="spgist-extensibility">
- <title>Extensibility</title>
-
- <para>
-  <acronym>SP-GiST</acronym> offers an interface with a high level of
-  abstraction, requiring the access method developer to implement only
-  methods specific to a given data type. The <acronym>SP-GiST</acronym> core
-  is responsible for efficient disk mapping and searching the tree structure.
-  It also takes care of concurrency and logging considerations.
- </para>
-
- <para>
-  Leaf tuples of an <acronym>SP-GiST</acronym> tree usually contain values
-  of the same data type as the indexed column, although it is also possible
-  for them to contain lossy representations of the indexed column.
-  Leaf tuples stored at the root level will directly represent
-  the original indexed data value, but leaf tuples at lower
-  levels might contain only a partial value, such as a suffix.
-  In that case the operator class support functions must be able to
-  reconstruct the original value using information accumulated from the
-  inner tuples that are passed through to reach the leaf level.
- </para>
-
- <para>
-  When an <acronym>SP-GiST</acronym> index is created with
-  <literal>INCLUDE</literal> columns, the values of those columns are also
-  stored in leaf tuples.  The <literal>INCLUDE</literal> columns are of no
-  concern to the <acronym>SP-GiST</acronym> operator class, so they are
-  not discussed further here.
- </para>
-
- <para>
-  Inner tuples are more complex, since they are branching points in the
-  search tree.  Each inner tuple contains a set of one or more
-  <firstterm>nodes</firstterm>, which represent groups of similar leaf values.
-  A node contains a downlink that leads either to another, lower-level inner
-  tuple, or to a short list of leaf tuples that all lie on the same index page.
-  Each node normally has a <firstterm>label</firstterm> that describes it; for example,
-  in a radix tree the node label could be the next character of the string
-  value.  (Alternatively, an operator class can omit the node labels, if it
-  works with a fixed set of nodes for all inner tuples;
-  see <xref linkend="spgist-null-labels"/>.)
-  Optionally, an inner tuple can have a <firstterm>prefix</firstterm> value
-  that describes all its members.  In a radix tree this could be the common
-  prefix of the represented strings.  The prefix value is not necessarily
-  really a prefix, but can be any data needed by the operator class;
-  for example, in a quad-tree it can store the central point that the four
-  quadrants are measured with respect to.  A quad-tree inner tuple would
-  then also contain four nodes corresponding to the quadrants around this
-  central point.
- </para>
-
- <para>
-  Some tree algorithms require knowledge of level (or depth) of the current
-  tuple, so the <acronym>SP-GiST</acronym> core provides the possibility for
-  operator classes to manage level counting while descending the tree.
-  There is also support for incrementally reconstructing the represented
-  value when that is needed, and for passing down additional data (called
-  <firstterm>traverse values</firstterm>) during a tree descent.
- </para>
-
- <note>
-  <para>
-   The <acronym>SP-GiST</acronym> core code takes care of null entries.
-   Although <acronym>SP-GiST</acronym> indexes do store entries for nulls
-   in indexed columns, this is hidden from the index operator class code:
-   no null index entries or search conditions will ever be passed to the
-   operator class methods.  (It is assumed that <acronym>SP-GiST</acronym>
-   operators are strict and so cannot succeed for null values.)  Null values
-   are therefore not discussed further here.
-  </para>
- </note>
-
- <para>
-  There are five user-defined methods that an index operator class for
-  <acronym>SP-GiST</acronym> must provide, and two are optional.  All five
-  mandatory methods follow the convention of accepting two <type>internal</type>
-  arguments, the first of which is a pointer to a C struct containing input
-  values for the support method, while the second argument is a pointer to a
-  C struct where output values must be placed.  Four of the mandatory methods just
-  return <type>void</type>, since all their results appear in the output struct; but
-  <function>leaf_consistent</function> returns a <type>boolean</type> result.
-  The methods must not modify any fields of their input structs.  In all
-  cases, the output struct is initialized to zeroes before calling the
-  user-defined method.  The optional sixth method <function>compress</function>
-  accepts a <type>datum</type> to be indexed as the only argument and returns a value suitable
-  for physical storage in a leaf tuple.  The optional seventh method
-  <function>options</function> accepts an <type>internal</type> pointer to a C struct, where
-  opclass-specific parameters should be placed, and returns <type>void</type>.
- </para>
-
- <para>
-  The five mandatory user-defined methods are:
- </para>
-
- <variablelist>
-    <varlistentry>
-     <term><function>config</function></term>
-     <listitem>
-      <para>
-       Returns static information about the index implementation, including
-       the data type OIDs of the prefix and node label data types.
-      </para>
-     <para>
-      The <acronym>SQL</acronym> declaration of the function must look like this:
-<programlisting>
-CREATE FUNCTION my_config(internal, internal) RETURNS void ...
-</programlisting>
-      The first argument is a pointer to a <structname>spgConfigIn</structname>
-      C struct, containing input data for the function.
-      The second argument is a pointer to a <structname>spgConfigOut</structname>
-      C struct, which the function must fill with result data.
-<programlisting>
-typedef struct spgConfigIn
-{
-    Oid         attType;        /* Data type to be indexed */
-} spgConfigIn;
-
-typedef struct spgConfigOut
-{
-    Oid         prefixType;     /* Data type of inner-tuple prefixes */
-    Oid         labelType;      /* Data type of inner-tuple node labels */
-    Oid         leafType;       /* Data type of leaf-tuple values */
-    bool        canReturnData;  /* Opclass can reconstruct original data */
-    bool        longValuesOK;   /* Opclass can cope with values &gt; 1 page */
-} spgConfigOut;
-</programlisting>
-
-      <structfield>attType</structfield> is passed in order to support polymorphic
-      index operator classes; for ordinary fixed-data-type operator classes, it
-      will always have the same value and so can be ignored.
-     </para>
-
-     <para>
-      For operator classes that do not use prefixes,
-      <structfield>prefixType</structfield> can be set to <literal>VOIDOID</literal>.
-      Likewise, for operator classes that do not use node labels,
-      <structfield>labelType</structfield> can be set to <literal>VOIDOID</literal>.
-      <structfield>canReturnData</structfield> should be set true if the operator class
-      is capable of reconstructing the originally-supplied index value.
-      <structfield>longValuesOK</structfield> should be set true only when the
-      <structfield>attType</structfield> is of variable length and the operator
-      class is capable of segmenting long values by repeated suffixing
-      (see <xref linkend="spgist-limits"/>).
-     </para>
-
-     <para>
-      <structfield>leafType</structfield> should match the index storage type
-      defined by the operator class's <structfield>opckeytype</structfield>
-      catalog entry.
-      (Note that <structfield>opckeytype</structfield> can be zero,
-      implying the storage type is the same as the operator class's input
-      type, which is the most common situation.)
-      For reasons of backward compatibility, the <function>config</function>
-      method can set <structfield>leafType</structfield> to some other value,
-      and that value will be used; but this is deprecated since the index
-      contents are then incorrectly identified in the catalogs.
-      Also, it's permissible to
-      leave <structfield>leafType</structfield> uninitialized (zero);
-      that is interpreted as meaning the index storage type derived from
-      <structfield>opckeytype</structfield>.
-     </para>
-
-     <para>
-      When <structfield>attType</structfield>
-      and <structfield>leafType</structfield> are different, the optional
-      method <function>compress</function> must be provided.
-      Method <function>compress</function> is responsible
-      for transformation of datums to be indexed from <structfield>attType</structfield>
-      to <structfield>leafType</structfield>.
-     </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>choose</function></term>
-     <listitem>
-      <para>
-        Chooses a method for inserting a new value into an inner tuple.
-      </para>
-
-     <para>
-      The <acronym>SQL</acronym> declaration of the function must look like this:
-<programlisting>
-CREATE FUNCTION my_choose(internal, internal) RETURNS void ...
-</programlisting>
-      The first argument is a pointer to a <structname>spgChooseIn</structname>
-      C struct, containing input data for the function.
-      The second argument is a pointer to a <structname>spgChooseOut</structname>
-      C struct, which the function must fill with result data.
-<programlisting>
-typedef struct spgChooseIn
-{
-    Datum       datum;          /* original datum to be indexed */
-    Datum       leafDatum;      /* current datum to be stored at leaf */
-    int         level;          /* current level (counting from zero) */
-
-    /* Data from current inner tuple */
-    bool        allTheSame;     /* tuple is marked all-the-same? */
-    bool        hasPrefix;      /* tuple has a prefix? */
-    Datum       prefixDatum;    /* if so, the prefix value */
-    int         nNodes;         /* number of nodes in the inner tuple */
-    Datum      *nodeLabels;     /* node label values (NULL if none) */
-} spgChooseIn;
-
-typedef enum spgChooseResultType
-{
-    spgMatchNode = 1,           /* descend into existing node */
-    spgAddNode,                 /* add a node to the inner tuple */
-    spgSplitTuple               /* split inner tuple (change its prefix) */
-} spgChooseResultType;
-
-typedef struct spgChooseOut
-{
-    spgChooseResultType resultType;     /* action code, see above */
-    union
-    {
-        struct                  /* results for spgMatchNode */
-        {
-            int         nodeN;      /* descend to this node (index from 0) */
-            int         levelAdd;   /* increment level by this much */
-            Datum       restDatum;  /* new leaf datum */
-        }           matchNode;
-        struct                  /* results for spgAddNode */
-        {
-            Datum       nodeLabel;  /* new node's label */
-            int         nodeN;      /* where to insert it (index from 0) */
-        }           addNode;
-        struct                  /* results for spgSplitTuple */
-        {
-            /* Info to form new upper-level inner tuple with one child tuple */
-            bool        prefixHasPrefix;    /* tuple should have a prefix? */
-            Datum       prefixPrefixDatum;  /* if so, its value */
-            int         prefixNNodes;       /* number of nodes */
-            Datum      *prefixNodeLabels;   /* their labels (or NULL for
-                                             * no labels) */
-            int         childNodeN;         /* which node gets child tuple */
-
-            /* Info to form new lower-level inner tuple with all old nodes */
-            bool        postfixHasPrefix;   /* tuple should have a prefix? */
-            Datum       postfixPrefixDatum; /* if so, its value */
-        }           splitTuple;
-    }           result;
-} spgChooseOut;
-</programlisting>
-
-       <structfield>datum</structfield> is the original datum of
-       <structname>spgConfigIn</structname>.<structfield>attType</structfield>
-       type that was to be inserted into the index.
-       <structfield>leafDatum</structfield> is a value of
-       <structname>spgConfigOut</structname>.<structfield>leafType</structfield>
-       type, which is initially a result of method
-       <function>compress</function> applied to <structfield>datum</structfield>
-       when method <function>compress</function> is provided, or the same value as
-       <structfield>datum</structfield> otherwise.
-       <structfield>leafDatum</structfield> can change at lower levels of the tree
-       if the <function>choose</function> or <function>picksplit</function>
-       methods change it.  When the insertion search reaches a leaf page,
-       the current value of <structfield>leafDatum</structfield> is what will be stored
-       in the newly created leaf tuple.
-       <structfield>level</structfield> is the current inner tuple's level, starting at
-       zero for the root level.
-       <structfield>allTheSame</structfield> is true if the current inner tuple is
-       marked as containing multiple equivalent nodes
-       (see <xref linkend="spgist-all-the-same"/>).
-       <structfield>hasPrefix</structfield> is true if the current inner tuple contains
-       a prefix; if so,
-       <structfield>prefixDatum</structfield> is its value.
-       <structfield>nNodes</structfield> is the number of child nodes contained in the
-       inner tuple, and
-       <structfield>nodeLabels</structfield> is an array of their label values, or
-       NULL if there are no labels.
-      </para>
-
-      <para>
-       The <function>choose</function> function can determine either that
-       the new value matches one of the existing child nodes, or that a new
-       child node must be added, or that the new value is inconsistent with
-       the tuple prefix and so the inner tuple must be split to create a
-       less restrictive prefix.
-      </para>
-
-      <para>
-       If the new value matches one of the existing child nodes,
-       set <structfield>resultType</structfield> to <literal>spgMatchNode</literal>.
-       Set <structfield>nodeN</structfield> to the index (from zero) of that node in
-       the node array.
-       Set <structfield>levelAdd</structfield> to the increment in
-       <structfield>level</structfield> caused by descending through that node,
-       or leave it as zero if the operator class does not use levels.
-       Set <structfield>restDatum</structfield> to equal <structfield>leafDatum</structfield>
-       if the operator class does not modify datums from one level to the
-       next, or otherwise set it to the modified value to be used as
-       <structfield>leafDatum</structfield> at the next level.
-      </para>
-
-      <para>
-       If a new child node must be added,
-       set <structfield>resultType</structfield> to <literal>spgAddNode</literal>.
-       Set <structfield>nodeLabel</structfield> to the label to be used for the new
-       node, and set <structfield>nodeN</structfield> to the index (from zero) at which
-       to insert the node in the node array.
-       After the node has been added, the <function>choose</function>
-       function will be called again with the modified inner tuple;
-       that call should result in an <literal>spgMatchNode</literal> result.
-      </para>
-
-      <para>
-       If the new value is inconsistent with the tuple prefix,
-       set <structfield>resultType</structfield> to <literal>spgSplitTuple</literal>.
-       This action moves all the existing nodes into a new lower-level
-       inner tuple, and replaces the existing inner tuple with a tuple
-       having a single downlink pointing to the new lower-level inner tuple.
-       Set <structfield>prefixHasPrefix</structfield> to indicate whether the new
-       upper tuple should have a prefix, and if so set
-       <structfield>prefixPrefixDatum</structfield> to the prefix value.  This new
-       prefix value must be sufficiently less restrictive than the original
-       to accept the new value to be indexed.
-       Set <structfield>prefixNNodes</structfield> to the number of nodes needed in the
-       new tuple, and set <structfield>prefixNodeLabels</structfield> to a palloc'd array
-       holding their labels, or to NULL if node labels are not required.
-       Note that the total size of the new upper tuple must be no more
-       than the total size of the tuple it is replacing; this constrains
-       the lengths of the new prefix and new labels.
-       Set <structfield>childNodeN</structfield> to the index (from zero) of the node
-       that will downlink to the new lower-level inner tuple.
-       Set <structfield>postfixHasPrefix</structfield> to indicate whether the new
-       lower-level inner tuple should have a prefix, and if so set
-       <structfield>postfixPrefixDatum</structfield> to the prefix value.  The
-       combination of these two prefixes and the downlink node's label
-       (if any) must have the same meaning as the original prefix, because
-       there is no opportunity to alter the node labels that are moved to
-       the new lower-level tuple, nor to change any child index entries.
-       After the node has been split, the <function>choose</function>
-       function will be called again with the replacement inner tuple.
-       That call may return an <literal>spgAddNode</literal> result, if no suitable
-       node was created by the <literal>spgSplitTuple</literal> action.  Eventually
-       <function>choose</function> must return <literal>spgMatchNode</literal> to
-       allow the insertion to descend to the next level.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>picksplit</function></term>
-     <listitem>
-      <para>
-       Decides how to create a new inner tuple over a set of leaf tuples.
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-<programlisting>
-CREATE FUNCTION my_picksplit(internal, internal) RETURNS void ...
-</programlisting>
-      The first argument is a pointer to a <structname>spgPickSplitIn</structname>
-      C struct, containing input data for the function.
-      The second argument is a pointer to a <structname>spgPickSplitOut</structname>
-      C struct, which the function must fill with result data.
-<programlisting>
-typedef struct spgPickSplitIn
-{
-    int         nTuples;        /* number of leaf tuples */
-    Datum      *datums;         /* their datums (array of length nTuples) */
-    int         level;          /* current level (counting from zero) */
-} spgPickSplitIn;
-
-typedef struct spgPickSplitOut
-{
-    bool        hasPrefix;      /* new inner tuple should have a prefix? */
-    Datum       prefixDatum;    /* if so, its value */
-
-    int         nNodes;         /* number of nodes for new inner tuple */
-    Datum      *nodeLabels;     /* their labels (or NULL for no labels) */
-
-    int        *mapTuplesToNodes;   /* node index for each leaf tuple */
-    Datum      *leafTupleDatums;    /* datum to store in each new leaf tuple */
-} spgPickSplitOut;
-</programlisting>
-
-       <structfield>nTuples</structfield> is the number of leaf tuples provided.
-       <structfield>datums</structfield> is an array of their datum values of
-       <structname>spgConfigOut</structname>.<structfield>leafType</structfield>
-       type.
-       <structfield>level</structfield> is the current level that all the leaf tuples
-       share, which will become the level of the new inner tuple.
-      </para>
-
-      <para>
-       Set <structfield>hasPrefix</structfield> to indicate whether the new inner
-       tuple should have a prefix, and if so set
-       <structfield>prefixDatum</structfield> to the prefix value.
-       Set <structfield>nNodes</structfield> to indicate the number of nodes that
-       the new inner tuple will contain, and
-       set <structfield>nodeLabels</structfield> to an array of their label values,
-       or to NULL if node labels are not required.
-       Set <structfield>mapTuplesToNodes</structfield> to an array that gives the index
-       (from zero) of the node that each leaf tuple should be assigned to.
-       Set <structfield>leafTupleDatums</structfield> to an array of the values to
-       be stored in the new leaf tuples (these will be the same as the
-       input <structfield>datums</structfield> if the operator class does not modify
-       datums from one level to the next).
-       Note that the <function>picksplit</function> function is
-       responsible for palloc'ing the
-       <structfield>nodeLabels</structfield>, <structfield>mapTuplesToNodes</structfield> and
-       <structfield>leafTupleDatums</structfield> arrays.
-      </para>
-
-      <para>
-       If more than one leaf tuple is supplied, it is expected that the
-       <function>picksplit</function> function will classify them into more than
-       one node; otherwise it is not possible to split the leaf tuples
-       across multiple pages, which is the ultimate purpose of this
-       operation.  Therefore, if the <function>picksplit</function> function
-       ends up placing all the leaf tuples in the same node, the core
-       SP-GiST code will override that decision and generate an inner
-       tuple in which the leaf tuples are assigned at random to several
-       identically-labeled nodes.  Such a tuple is marked
-       <literal>allTheSame</literal> to signify that this has happened.  The
-       <function>choose</function> and <function>inner_consistent</function> functions
-       must take suitable care with such inner tuples.
-       See <xref linkend="spgist-all-the-same"/> for more information.
-      </para>
-
-      <para>
-       <function>picksplit</function> can be applied to a single leaf tuple only
-       in the case that the <function>config</function> function set
-       <structfield>longValuesOK</structfield> to true and a larger-than-a-page input
-       value has been supplied.  In this case the point of the operation is
-       to strip off a prefix and produce a new, shorter leaf datum value.
-       The call will be repeated until a leaf datum short enough to fit on
-       a page has been produced.  See <xref linkend="spgist-limits"/> for
-       more information.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>inner_consistent</function></term>
-     <listitem>
-      <para>
-       Returns set of nodes (branches) to follow during tree search.
-      </para>
-
-      <para>
-       The <acronym>SQL</acronym> declaration of the function must look like this:
-<programlisting>
-CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...
-</programlisting>
-      The first argument is a pointer to a <structname>spgInnerConsistentIn</structname>
-      C struct, containing input data for the function.
-      The second argument is a pointer to a <structname>spgInnerConsistentOut</structname>
-      C struct, which the function must fill with result data.
-
-<programlisting>
-typedef struct spgInnerConsistentIn
-{
-    ScanKey     scankeys;       /* array of operators and comparison values */
-    ScanKey     orderbys;       /* array of ordering operators and comparison
-                                 * values */
-    int         nkeys;          /* length of scankeys array */
-    int         norderbys;      /* length of orderbys array */
-
-    Datum       reconstructedValue;     /* value reconstructed at parent */
-    void       *traversalValue; /* opclass-specific traverse value */
-    MemoryContext traversalMemoryContext;   /* put new traverse values here */
-    int         level;          /* current level (counting from zero) */
-    bool        returnData;     /* original data must be returned? */
-
-    /* Data from current inner tuple */
-    bool        allTheSame;     /* tuple is marked all-the-same? */
-    bool        hasPrefix;      /* tuple has a prefix? */
-    Datum       prefixDatum;    /* if so, the prefix value */
-    int         nNodes;         /* number of nodes in the inner tuple */
-    Datum      *nodeLabels;     /* node label values (NULL if none) */
-} spgInnerConsistentIn;
-
-typedef struct spgInnerConsistentOut
-{
-    int         nNodes;         /* number of child nodes to be visited */
-    int        *nodeNumbers;    /* their indexes in the node array */
-    int        *levelAdds;      /* increment level by this much for each */
-    Datum      *reconstructedValues;    /* associated reconstructed values */
-    void      **traversalValues;        /* opclass-specific traverse values */
-    double    **distances;              /* associated distances */
-} spgInnerConsistentOut;
-</programlisting>
-
-       The array <structfield>scankeys</structfield>, of length <structfield>nkeys</structfield>,
-       describes the index search condition(s).  These conditions are
-       combined with AND &mdash; only index entries that satisfy all of
-       them are interesting.  (Note that <structfield>nkeys</structfield> = 0 implies
-       that all index entries satisfy the query.)  Usually the consistent
-       function only cares about the <structfield>sk_strategy</structfield> and
-       <structfield>sk_argument</structfield> fields of each array entry, which
-       respectively give the indexable operator and comparison value.
-       In particular it is not necessary to check <structfield>sk_flags</structfield> to
-       see if the comparison value is NULL, because the SP-GiST core code
-       will filter out such conditions.
-       The array <structfield>orderbys</structfield>, of length <structfield>norderbys</structfield>,
-       describes ordering operators (if any) in the same manner.
-       <structfield>reconstructedValue</structfield> is the value reconstructed for the
-       parent tuple; it is <literal>(Datum) 0</literal> at the root level or if the
-       <function>inner_consistent</function> function did not provide a value at the
-       parent level.
-       <structfield>traversalValue</structfield> is a pointer to any traverse data
-       passed down from the previous call of <function>inner_consistent</function>
-       on the parent index tuple, or NULL at the root level.
-       <structfield>traversalMemoryContext</structfield> is the memory context in which
-       to store output traverse values (see below).
-       <structfield>level</structfield> is the current inner tuple's level, starting at
-       zero for the root level.
-       <structfield>returnData</structfield> is <literal>true</literal> if reconstructed data is
-       required for this query; this will only be so if the
-       <function>config</function> function asserted <structfield>canReturnData</structfield>.
-       <structfield>allTheSame</structfield> is true if the current inner tuple is
-       marked <quote>all-the-same</quote>; in this case all the nodes have the
-       same label (if any) and so either all or none of them match the query
-       (see <xref linkend="spgist-all-the-same"/>).
-       <structfield>hasPrefix</structfield> is true if the current inner tuple contains
-       a prefix; if so,
-       <structfield>prefixDatum</structfield> is its value.
-       <structfield>nNodes</structfield> is the number of child nodes contained in the
-       inner tuple, and
-       <structfield>nodeLabels</structfield> is an array of their label values, or
-       NULL if the nodes do not have labels.
-      </para>
-
-      <para>
-       <structfield>nNodes</structfield> must be set to the number of child nodes that
-       need to be visited by the search, and
-       <structfield>nodeNumbers</structfield> must be set to an array of their indexes.
-       If the operator class keeps track of levels, set
-       <structfield>levelAdds</structfield> to an array of the level increments
-       required when descending to each node to be visited.  (Often these
-       increments will be the same for all the nodes, but that's not
-       necessarily so, so an array is used.)
-       If value reconstruction is needed, set
-       <structfield>reconstructedValues</structfield> to an array of the values
-       reconstructed for each child node to be visited; otherwise, leave
-       <structfield>reconstructedValues</structfield> as NULL.
-       The reconstructed values are assumed to be of type
-       <structname>spgConfigOut</structname>.<structfield>leafType</structfield>.
-       (However, since the core system will do nothing with them except
-       possibly copy them, it is sufficient for them to have the
-       same <literal>typlen</literal> and <literal>typbyval</literal>
-       properties as <structfield>leafType</structfield>.)
-       If ordered search is performed, set <structfield>distances</structfield>
-       to an array of distance values according to <structfield>orderbys</structfield>
-       array (nodes with lowest distances will be processed first).  Leave it
-       NULL otherwise.
-       If it is desired to pass down additional out-of-band information
-       (<quote>traverse values</quote>) to lower levels of the tree search,
-       set <structfield>traversalValues</structfield> to an array of the appropriate
-       traverse values, one for each child node to be visited; otherwise,
-       leave <structfield>traversalValues</structfield> as NULL.
-       Note that the <function>inner_consistent</function> function is
-       responsible for palloc'ing the
-       <structfield>nodeNumbers</structfield>, <structfield>levelAdds</structfield>,
-       <structfield>distances</structfield>,
-       <structfield>reconstructedValues</structfield>, and
-       <structfield>traversalValues</structfield> arrays in the current memory context.
-       However, any output traverse values pointed to by
-       the <structfield>traversalValues</structfield> array should be allocated
-       in <structfield>traversalMemoryContext</structfield>.
-       Each traverse value must be a single palloc'd chunk.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>leaf_consistent</function></term>
-     <listitem>
-      <para>
-       Returns true if a leaf tuple satisfies a query.
-      </para>
-
-      <para>
-       The <acronym>SQL</acronym> declaration of the function must look like this:
-<programlisting>
-CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ...
-</programlisting>
-      The first argument is a pointer to a <structname>spgLeafConsistentIn</structname>
-      C struct, containing input data for the function.
-      The second argument is a pointer to a <structname>spgLeafConsistentOut</structname>
-      C struct, which the function must fill with result data.
-<programlisting>
-typedef struct spgLeafConsistentIn
-{
-    ScanKey     scankeys;       /* array of operators and comparison values */
-    ScanKey     orderbys;       /* array of ordering operators and comparison
-                                 * values */
-    int         nkeys;          /* length of scankeys array */
-    int         norderbys;      /* length of orderbys array */
-
-    Datum       reconstructedValue;     /* value reconstructed at parent */
-    void       *traversalValue; /* opclass-specific traverse value */
-    int         level;          /* current level (counting from zero) */
-    bool        returnData;     /* original data must be returned? */
-
-    Datum       leafDatum;      /* datum in leaf tuple */
-} spgLeafConsistentIn;
-
-typedef struct spgLeafConsistentOut
-{
-    Datum       leafValue;        /* reconstructed original data, if any */
-    bool        recheck;          /* set true if operator must be rechecked */
-    bool        recheckDistances; /* set true if distances must be rechecked */
-    double     *distances;        /* associated distances */
-} spgLeafConsistentOut;
-</programlisting>
-
-       The array <structfield>scankeys</structfield>, of length <structfield>nkeys</structfield>,
-       describes the index search condition(s).  These conditions are
-       combined with AND &mdash; only index entries that satisfy all of
-       them satisfy the query.  (Note that <structfield>nkeys</structfield> = 0 implies
-       that all index entries satisfy the query.)  Usually the consistent
-       function only cares about the <structfield>sk_strategy</structfield> and
-       <structfield>sk_argument</structfield> fields of each array entry, which
-       respectively give the indexable operator and comparison value.
-       In particular it is not necessary to check <structfield>sk_flags</structfield> to
-       see if the comparison value is NULL, because the SP-GiST core code
-       will filter out such conditions.
-       The array <structfield>orderbys</structfield>, of length <structfield>norderbys</structfield>,
-       describes the ordering operators in the same manner.
-       <structfield>reconstructedValue</structfield> is the value reconstructed for the
-       parent tuple; it is <literal>(Datum) 0</literal> at the root level or if the
-       <function>inner_consistent</function> function did not provide a value at the
-       parent level.
-       <structfield>traversalValue</structfield> is a pointer to any traverse data
-       passed down from the previous call of <function>inner_consistent</function>
-       on the parent index tuple, or NULL at the root level.
-       <structfield>level</structfield> is the current leaf tuple's level, starting at
-       zero for the root level.
-       <structfield>returnData</structfield> is <literal>true</literal> if reconstructed data is
-       required for this query; this will only be so if the
-       <function>config</function> function asserted <structfield>canReturnData</structfield>.
-       <structfield>leafDatum</structfield> is the key value of
-       <structname>spgConfigOut</structname>.<structfield>leafType</structfield>
-       stored in the current leaf tuple.
-      </para>
-
-      <para>
-       The function must return <literal>true</literal> if the leaf tuple matches the
-       query, or <literal>false</literal> if not.  In the <literal>true</literal> case,
-       if <structfield>returnData</structfield> is <literal>true</literal> then
-       <structfield>leafValue</structfield> must be set to the value (of type
-       <structname>spgConfigIn</structname>.<structfield>attType</structfield>)
-       originally supplied to be indexed for this leaf tuple.  Also,
-       <structfield>recheck</structfield> may be set to <literal>true</literal> if the match
-       is uncertain and so the operator(s) must be re-applied to the actual
-       heap tuple to verify the match.
-       If ordered search is performed, set <structfield>distances</structfield>
-       to an array of distance values according to <structfield>orderbys</structfield>
-       array.  Leave it NULL otherwise.  If at least one of returned distances
-       is not exact, set <structfield>recheckDistances</structfield> to true.
-       In this case, the executor will calculate the exact distances after
-       fetching the tuple from the heap, and will reorder the tuples if needed.
-      </para>
-     </listitem>
-    </varlistentry>
-   </variablelist>
-
- <para>
-  The optional user-defined methods are:
- </para>
-
- <variablelist>
-    <varlistentry>
-     <term><function>Datum compress(Datum in)</function></term>
-     <listitem>
-      <para>
-       Converts a data item into a format suitable for physical storage in
-       a leaf tuple of the index.  It accepts a value of type
-       <structname>spgConfigIn</structname>.<structfield>attType</structfield>
-       and returns a value of type
-       <structname>spgConfigOut</structname>.<structfield>leafType</structfield>.
-       The output value must not contain an out-of-line TOAST pointer.
-      </para>
-
-      <para>
-       Note: the <function>compress</function> method is only applied to
-       values to be stored.  The consistent methods receive query
-       <structfield>scankeys</structfield> unchanged, without transformation
-       using <function>compress</function>.
-      </para>
-     </listitem>
-    </varlistentry>
-
-    <varlistentry>
-     <term><function>options</function></term>
-     <listitem>
-      <para>
-       Defines a set of user-visible parameters that control operator class
-       behavior.
-      </para>
-
-      <para>
-        The <acronym>SQL</acronym> declaration of the function must look like this:
-
-<programlisting>
-CREATE OR REPLACE FUNCTION my_options(internal)
-RETURNS void
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT;
-</programlisting>
-      </para>
-
-      <para>
-       The function is passed a pointer to a <structname>local_relopts</structname>
-       struct, which needs to be filled with a set of operator class
-       specific options.  The options can be accessed from other support
-       functions using the <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
-       <literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
-      </para>
-
-      <para>
-       Since the representation of the key in <acronym>SP-GiST</acronym> is
-       flexible, it may depend on user-specified parameters.
-      </para>
-     </listitem>
-    </varlistentry>
-  </variablelist>
-
-  <para>
-   All the SP-GiST support methods are normally called in a short-lived
-   memory context; that is, <varname>CurrentMemoryContext</varname> will be reset
-   after processing of each tuple.  It is therefore not very important to
-   worry about pfree'ing everything you palloc.  (The <function>config</function>
-   method is an exception: it should try to avoid leaking memory.  But
-   usually the <function>config</function> method need do nothing but assign
-   constants into the passed parameter struct.)
-  </para>
-
-  <para>
-   If the indexed column is of a collatable data type, the index collation
-   will be passed to all the support methods, using the standard
-   <function>PG_GET_COLLATION()</function> mechanism.
-  </para>
-
-</sect1>
-
-<sect1 id="spgist-implementation">
- <title>Implementation</title>
-
-  <para>
-   This section covers implementation details and other tricks that are
-   useful for implementers of <acronym>SP-GiST</acronym> operator classes to
-   know.
-  </para>
-
- <sect2 id="spgist-limits">
-  <title>SP-GiST Limits</title>
-
-  <para>
-   Individual leaf tuples and inner tuples must fit on a single index page
-   (8kB by default).  Therefore, when indexing values of variable-length
-   data types, long values can only be supported by methods such as radix
-   trees, in which each level of the tree includes a prefix that is short
-   enough to fit on a page, and the final leaf level includes a suffix also
-   short enough to fit on a page.  The operator class should set
-   <structfield>longValuesOK</structfield> to true only if it is prepared to arrange for
-   this to happen.  Otherwise, the <acronym>SP-GiST</acronym> core will
-   reject any request to index a value that is too large to fit
-   on an index page.
-  </para>
-
-  <para>
-   Likewise, it is the operator class's responsibility that inner tuples
-   do not grow too large to fit on an index page; this limits the number
-   of child nodes that can be used in one inner tuple, as well as the
-   maximum size of a prefix value.
-  </para>
-
-  <para>
-   Another limitation is that when an inner tuple's node points to a set
-   of leaf tuples, those tuples must all be in the same index page.
-   (This is a design decision to reduce seeking and save space in the
-   links that chain such tuples together.)  If the set of leaf tuples
-   grows too large for a page, a split is performed and an intermediate
-   inner tuple is inserted.  For this to fix the problem, the new inner
-   tuple <emphasis>must</emphasis> divide the set of leaf values into more than one
-   node group.  If the operator class's <function>picksplit</function> function
-   fails to do that, the <acronym>SP-GiST</acronym> core resorts to
-   extraordinary measures described in <xref linkend="spgist-all-the-same"/>.
-  </para>
-
-  <para>
-   When <structfield>longValuesOK</structfield> is true, it is expected
-   that successive levels of the <acronym>SP-GiST</acronym> tree will
-   absorb more and more information into the prefixes and node labels of
-   the inner tuples, making the required leaf datum smaller and smaller,
-   so that eventually it will fit on a page.
-   To prevent bugs in operator classes from causing infinite insertion
-   loops, the <acronym>SP-GiST</acronym> core will raise an error if the
-   leaf datum does not become any smaller within ten cycles
-   of <function>choose</function> method calls.
-  </para>
- </sect2>
-
- <sect2 id="spgist-null-labels">
-  <title>SP-GiST Without Node Labels</title>
-
-  <para>
-   Some tree algorithms use a fixed set of nodes for each inner tuple;
-   for example, in a quad-tree there are always exactly four nodes
-   corresponding to the four quadrants around the inner tuple's centroid
-   point.  In such a case the code typically works with the nodes by
-   number, and there is no need for explicit node labels.  To suppress
-   node labels (and thereby save some space), the <function>picksplit</function>
-   function can return NULL for the <structfield>nodeLabels</structfield> array,
-   and likewise the <function>choose</function> function can return NULL for
-   the <structfield>prefixNodeLabels</structfield> array during
-   a <literal>spgSplitTuple</literal> action.
-   This will in turn result in <structfield>nodeLabels</structfield> being NULL during
-   subsequent calls to <function>choose</function> and <function>inner_consistent</function>.
-   In principle, node labels could be used for some inner tuples and omitted
-   for others in the same index.
-  </para>
-
-  <para>
-   When working with an inner tuple having unlabeled nodes, it is an error
-   for <function>choose</function> to return <literal>spgAddNode</literal>, since the set
-   of nodes is supposed to be fixed in such cases.
-  </para>
- </sect2>
-
- <sect2 id="spgist-all-the-same">
-  <title><quote>All-the-Same</quote> Inner Tuples</title>
-
-  <para>
-   The <acronym>SP-GiST</acronym> core can override the results of the
-   operator class's <function>picksplit</function> function when
-   <function>picksplit</function> fails to divide the supplied leaf values into
-   at least two node categories.  When this happens, the new inner tuple
-   is created with multiple nodes that each have the same label (if any)
-   that <function>picksplit</function> gave to the one node it did use, and the
-   leaf values are divided at random among these equivalent nodes.
-   The <literal>allTheSame</literal> flag is set on the inner tuple to warn the
-   <function>choose</function> and <function>inner_consistent</function> functions that the
-   tuple does not have the node set that they might otherwise expect.
-  </para>
-
-  <para>
-   When dealing with an <literal>allTheSame</literal> tuple, a <function>choose</function>
-   result of <literal>spgMatchNode</literal> is interpreted to mean that the new
-   value can be assigned to any of the equivalent nodes; the core code will
-   ignore the supplied  <structfield>nodeN</structfield> value and descend into one
-   of the nodes at random (so as to keep the tree balanced).  It is an
-   error for <function>choose</function> to return <literal>spgAddNode</literal>, since
-   that would make the nodes not all equivalent; the
-   <literal>spgSplitTuple</literal> action must be used if the value to be inserted
-   doesn't match the existing nodes.
-  </para>
-
-  <para>
-   When dealing with an <literal>allTheSame</literal> tuple, the
-   <function>inner_consistent</function> function should return either all or none
-   of the nodes as targets for continuing the index search, since they are
-   all equivalent.  This may or may not require any special-case code,
-   depending on how much the <function>inner_consistent</function> function normally
-   assumes about the meaning of the nodes.
-  </para>
- </sect2>
-
-</sect1>
-
-<sect1 id="spgist-examples">
- <title>Examples</title>
-
- <para>
-  The <productname>PostgreSQL</productname> source distribution includes
-  several examples of index operator classes for <acronym>SP-GiST</acronym>,
-  as described in <xref linkend="spgist-builtin-opclasses-table"/>.  Look
-  into <filename>src/backend/access/spgist/</filename>
-  and <filename>src/backend/utils/adt/</filename> to see the code.
- </para>
-
-</sect1>
-
-</chapter>
-- 
2.39.3 (Apple Git-145)

