From f23a5630ebc797219b62797f566dec9f65090e03 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 8 Apr 2014 15:46:14 -0400
Subject: [PATCH] Add an in-core GiST index opclass for inet/cidr types.

This operator class can accelerate subnet/supernet tests as well as
btree-equivalent ordered comparisons.  It also handles a new network
operator inet && inet (overlaps, a/k/a "is supernet or subnet of"),
which is expected to be useful in exclusion constraints.

Ideally this opclass would be the default for GiST with inet/cidr data,
but we can't mark it that way until we figure out how to do a more or
less graceful transition from the current situation, in which the
really-completely-bogus inet/cidr opclasses in contrib/btree_gist are
marked as default.  Having the opclass in core and not default is better
than not having it at all, though.

While at it, add new documentation sections to allow us to officially
document GiST/GIN/SP-GiST opclasses, something there was never a clear
place to do before.  I filled these in with some simple tables listing
the existing opclasses and the operators they support, but there's
certainly scope to put more information there.

Emre Hasegeli, reviewed by Andreas Karlsson, further hacking by me
---
 doc/src/sgml/func.sgml                   |  14 +-
 doc/src/sgml/gin.sgml                    | 359 +++++++++++
 doc/src/sgml/gist.sgml                   | 178 +++++
 doc/src/sgml/indices.sgml                |  28 +-
 doc/src/sgml/spgist.sgml                 |  87 +++
 src/backend/utils/adt/Makefile           |   3 +-
 src/backend/utils/adt/network.c          | 117 ++--
 src/backend/utils/adt/network_gist.c     | 789 +++++++++++++++++++++++
 src/backend/utils/adt/network_selfuncs.c |  32 +
 src/include/catalog/catversion.h         |   2 +-
 src/include/catalog/pg_amop.h            |  15 +
 src/include/catalog/pg_amproc.h          |   7 +
 src/include/catalog/pg_opclass.h         |   1 +
 src/include/catalog/pg_operator.h        |  10 +-
 src/include/catalog/pg_opfamily.h        |   1 +
 src/include/catalog/pg_proc.h            |  23 +
 src/include/utils/builtins.h             |   1 +
 src/include/utils/inet.h                 |  55 ++
 src/test/regress/expected/inet.out       | 172 ++++-
 src/test/regress/expected/opr_sanity.out |  11 +-
 src/test/regress/sql/inet.sql            |  23 +-
 21 files changed, 1839 insertions(+), 89 deletions(-)
 create mode 100644 src/backend/utils/adt/network_gist.c
 create mode 100644 src/backend/utils/adt/network_selfuncs.c

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 73764f2bedc..0809a6d2e92 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -8434,8 +8434,9 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple
    <xref linkend="cidr-inet-operators-table"> shows the operators
    available for the <type>cidr</type> and <type>inet</type> types.
    The operators <literal>&lt;&lt;</literal>,
-   <literal>&lt;&lt;=</literal>, <literal>&gt;&gt;</literal>, and
-   <literal>&gt;&gt;=</literal> test for subnet inclusion.  They
+   <literal>&lt;&lt;=</literal>, <literal>&gt;&gt;</literal>,
+   <literal>&gt;&gt;=</literal>, and <literal>&amp;&amp;</literal>
+   test for subnet inclusion.  They
    consider only the network parts of the two addresses (ignoring any
    host part) and determine whether one network is identical to
    or a subnet of the other.
@@ -8484,12 +8485,12 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple
        </row>
        <row>
         <entry> <literal>&lt;&lt;</literal> </entry>
-        <entry>is contained within</entry>
+        <entry>is contained by</entry>
         <entry><literal>inet '192.168.1.5' &lt;&lt; inet '192.168.1/24'</literal></entry>
        </row>
        <row>
         <entry> <literal>&lt;&lt;=</literal> </entry>
-        <entry>is contained within or equals</entry>
+        <entry>is contained by or equals</entry>
         <entry><literal>inet '192.168.1/24' &lt;&lt;= inet '192.168.1/24'</literal></entry>
        </row>
        <row>
@@ -8502,6 +8503,11 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple
         <entry>contains or equals</entry>
         <entry><literal>inet '192.168.1/24' &gt;&gt;= inet '192.168.1/24'</literal></entry>
        </row>
+       <row>
+        <entry> <literal>&amp;&amp;</literal> </entry>
+        <entry>contains or is contained by</entry>
+        <entry><literal>inet '192.168.1/24' &amp;&amp; inet '192.168.1.80/28'</literal></entry>
+       </row>
        <row>
         <entry> <literal>~</literal> </entry>
         <entry>bitwise NOT</entry>
diff --git a/doc/src/sgml/gin.sgml b/doc/src/sgml/gin.sgml
index 561608f8fa8..576ad3005aa 100644
--- a/doc/src/sgml/gin.sgml
+++ b/doc/src/sgml/gin.sgml
@@ -62,6 +62,365 @@
  </para>
 </sect1>
 
+<sect1 id="gin-builtin-opclasses">
+ <title>Built-in Operator Classes</title>
+
+ <para>
+  The core <productname>PostgreSQL</> 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="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Indexed Data Type</entry>
+      <entry>Indexable Operators</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry><literal>_abstime_ops</></entry>
+      <entry><type>abstime[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_bit_ops</></entry>
+      <entry><type>bit[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_bool_ops</></entry>
+      <entry><type>boolean[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_bpchar_ops</></entry>
+      <entry><type>character[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_bytea_ops</></entry>
+      <entry><type>bytea[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_char_ops</></entry>
+      <entry><type>"char"[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_cidr_ops</></entry>
+      <entry><type>cidr[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_date_ops</></entry>
+      <entry><type>date[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_float4_ops</></entry>
+      <entry><type>float4[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_float8_ops</></entry>
+      <entry><type>float8[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_inet_ops</></entry>
+      <entry><type>inet[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_int2_ops</></entry>
+      <entry><type>smallint[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_int4_ops</></entry>
+      <entry><type>integer[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_int8_ops</></entry>
+      <entry><type>bigint[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_interval_ops</></entry>
+      <entry><type>interval[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_macaddr_ops</></entry>
+      <entry><type>macaddr[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_money_ops</></entry>
+      <entry><type>money[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_name_ops</></entry>
+      <entry><type>name[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_numeric_ops</></entry>
+      <entry><type>numeric[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_oid_ops</></entry>
+      <entry><type>oid[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_oidvector_ops</></entry>
+      <entry><type>oidvector[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_reltime_ops</></entry>
+      <entry><type>reltime[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_text_ops</></entry>
+      <entry><type>text[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_time_ops</></entry>
+      <entry><type>time[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_timestamp_ops</></entry>
+      <entry><type>timestamp[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_timestamptz_ops</></entry>
+      <entry><type>timestamp with time zone[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_timetz_ops</></entry>
+      <entry><type>time with time zone[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_tinterval_ops</></entry>
+      <entry><type>tinterval[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_varbit_ops</></entry>
+      <entry><type>bit varying[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>_varchar_ops</></entry>
+      <entry><type>character varying[]</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>jsonb_ops</></entry>
+      <entry><type>jsonb</></entry>
+      <entry>
+       <literal>?</>
+       <literal>?&amp;</>
+       <literal>?|</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>jsonb_hash_ops</></entry>
+      <entry><type>jsonb</></entry>
+      <entry>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>tsvector_ops</></entry>
+      <entry><type>tsvector</></entry>
+      <entry>
+       <literal>@@</>
+       <literal>@@@</>
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+ <para>
+  Of the two operator classes for type <type>jsonb</>, <literal>jsonb_ops</>
+  is the default.  <literal>jsonb_hash_ops</> supports fewer operators but
+  will work with larger indexed values than <literal>jsonb_ops</> can support.
+ </para>
+
+</sect1>
+
 <sect1 id="gin-extensibility">
  <title>Extensibility</title>
 
diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml
index ed0bc54f52d..0158b1759e8 100644
--- a/doc/src/sgml/gist.sgml
+++ b/doc/src/sgml/gist.sgml
@@ -40,6 +40,184 @@
 
 </sect1>
 
+<sect1 id="gist-builtin-opclasses">
+ <title>Built-in Operator Classes</title>
+
+ <para>
+  The core <productname>PostgreSQL</> 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="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Indexed Data Type</entry>
+      <entry>Indexable Operators</entry>
+      <entry>Ordering Operators</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry><literal>box_ops</></entry>
+      <entry><type>box</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&amp;&gt;</>
+       <literal>&amp;&lt;</>
+       <literal>&amp;&lt;|</>
+       <literal>&gt;&gt;</>
+       <literal>&lt;&lt;</>
+       <literal>&lt;&lt;|</>
+       <literal>&lt;@</>
+       <literal>@&gt;</>
+       <literal>@</>
+       <literal>|&amp;&gt;</>
+       <literal>|&gt;&gt;</>
+       <literal>~</>
+       <literal>~=</>
+      </entry>
+      <entry>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>circle_ops</></entry>
+      <entry><type>circle</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&amp;&gt;</>
+       <literal>&amp;&lt;</>
+       <literal>&amp;&lt;|</>
+       <literal>&gt;&gt;</>
+       <literal>&lt;&lt;</>
+       <literal>&lt;&lt;|</>
+       <literal>&lt;@</>
+       <literal>@&gt;</>
+       <literal>@</>
+       <literal>|&amp;&gt;</>
+       <literal>|&gt;&gt;</>
+       <literal>~</>
+       <literal>~=</>
+      </entry>
+      <entry>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>inet_ops</></entry>
+      <entry><type>inet</>, <type>cidr</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&gt;&gt;</>
+       <literal>&gt;&gt;=</>
+       <literal>&gt;</>
+       <literal>&gt;=</>
+       <literal>&lt;&gt;</>
+       <literal>&lt;&lt;</>
+       <literal>&lt;&lt;=</>
+       <literal>&lt;</>
+       <literal>&lt;=</>
+       <literal>=</>
+      </entry>
+      <entry>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>point_ops</></entry>
+      <entry><type>point</></entry>
+      <entry>
+       <literal>&gt;&gt;</>
+       <literal>&gt;^</>
+       <literal>&lt;&lt;</>
+       <literal>&lt;@</>
+       <literal>&lt;@</>
+       <literal>&lt;@</>
+       <literal>&lt;^</>
+       <literal>~=</>
+      </entry>
+      <entry>
+       <literal>&lt;-&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>poly_ops</></entry>
+      <entry><type>polygon</></entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&amp;&gt;</>
+       <literal>&amp;&lt;</>
+       <literal>&amp;&lt;|</>
+       <literal>&gt;&gt;</>
+       <literal>&lt;&lt;</>
+       <literal>&lt;&lt;|</>
+       <literal>&lt;@</>
+       <literal>@&gt;</>
+       <literal>@</>
+       <literal>|&amp;&gt;</>
+       <literal>|&gt;&gt;</>
+       <literal>~</>
+       <literal>~=</>
+      </entry>
+      <entry>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>range_ops</></entry>
+      <entry>any range type</entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&amp;&gt;</>
+       <literal>&amp;&lt;</>
+       <literal>&gt;&gt;</>
+       <literal>&lt;&lt;</>
+       <literal>&lt;@</>
+       <literal>-|-</>
+       <literal>=</>
+       <literal>@&gt;</>
+       <literal>@&gt;</>
+      </entry>
+      <entry>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>tsquery_ops</></entry>
+      <entry><type>tsquery</></entry>
+      <entry>
+       <literal>&lt;@</>
+       <literal>@&gt;</>
+      </entry>
+      <entry>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>tsvector_ops</></entry>
+      <entry><type>tsvector</></entry>
+      <entry>
+       <literal>@@</>
+      </entry>
+      <entry>
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+ <para>
+  For historical reasons, the <literal>inet_ops</> operator class is
+  not the default class for types <type>inet</> and <type>cidr</>.
+  To use it, mention the class name in <command>CREATE INDEX</>,
+  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>
 
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index b1c8f227185..64530a11c86 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -239,6 +239,8 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
 
    (See <xref linkend="functions-geometry"> for the meaning of
    these operators.)
+   The GiST operator classes included in the standard distribution are
+   documented in <xref linkend="gist-builtin-opclasses-table">.
    Many other GiST operator
    classes are available in the <literal>contrib</> collection or as separate
    projects.  For more information see <xref linkend="GiST">.
@@ -253,6 +255,8 @@ SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;
 </programlisting>
    which finds the ten places closest to a given target point.  The ability
    to do this is again dependent on the particular operator class being used.
+   In <xref linkend="gist-builtin-opclasses-table">, operators that can be
+   used in this way are listed in the column <quote>Ordering Operators</>.
   </para>
 
   <para>
@@ -283,6 +287,8 @@ SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;
 
    (See <xref linkend="functions-geometry"> for the meaning of
    these operators.)
+   The SP-GiST operator classes included in the standard distribution are
+   documented in <xref linkend="spgist-builtin-opclasses-table">.
    For more information see <xref linkend="SPGiST">.
   </para>
 
@@ -314,6 +320,8 @@ SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;
 
    (See <xref linkend="functions-array"> for the meaning of
    these operators.)
+   The GIN operator classes included in the standard distribution are
+   documented in <xref linkend="gin-builtin-opclasses-table">.
    Many other GIN operator
    classes are available in the <literal>contrib</> collection or as separate
    projects.  For more information see <xref linkend="GIN">.
@@ -1003,7 +1011,9 @@ CREATE INDEX test_index ON test_table (col varchar_pattern_ops);
 
 <programlisting>
 SELECT am.amname AS index_method,
-       opc.opcname AS opclass_name
+       opc.opcname AS opclass_name,
+       opc.opcintype::regtype AS indexed_type,
+       opc.opcdefault AS is_default
     FROM pg_am am, pg_opclass opc
     WHERE opc.opcmethod = am.oid
     ORDER BY index_method, opclass_name;
@@ -1020,6 +1030,22 @@ SELECT am.amname AS index_method,
    associated with any single class within the family.
   </para>
 
+  <para>
+    This expanded version of the previous query shows the operator family
+    each operator class belongs to:
+<programlisting>
+SELECT am.amname AS index_method,
+       opc.opcname AS opclass_name,
+       opf.opfname AS opfamily_name,
+       opc.opcintype::regtype AS indexed_type,
+       opc.opcdefault AS is_default
+    FROM pg_am am, pg_opclass opc, pg_opfamily opf
+    WHERE opc.opcmethod = am.oid AND
+          opc.opcfamily = opf.oid
+    ORDER BY index_method, opclass_name;
+</programlisting>
+  </para>
+
   <para>
     This query shows all defined operator families and all
     the operators included in each family:
diff --git a/doc/src/sgml/spgist.sgml b/doc/src/sgml/spgist.sgml
index a043ffb06c4..56827e520dd 100644
--- a/doc/src/sgml/spgist.sgml
+++ b/doc/src/sgml/spgist.sgml
@@ -53,6 +53,93 @@
 
 </sect1>
 
+<sect1 id="spgist-builtin-opclasses">
+ <title>Built-in Operator Classes</title>
+
+ <para>
+  The core <productname>PostgreSQL</> 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>Indexed Data Type</entry>
+      <entry>Indexable Operators</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry><literal>kd_point_ops</></entry>
+      <entry><type>point</></entry>
+      <entry>
+       <literal>&lt;&lt;</>
+       <literal>&lt;@</>
+       <literal>&lt;^</>
+       <literal>&gt;&gt;</>
+       <literal>&gt;^</>
+       <literal>~=</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>quad_point_ops</></entry>
+      <entry><type>point</></entry>
+      <entry>
+       <literal>&lt;&lt;</>
+       <literal>&lt;@</>
+       <literal>&lt;^</>
+       <literal>&gt;&gt;</>
+       <literal>&gt;^</>
+       <literal>~=</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>range_ops</></entry>
+      <entry>any range type</entry>
+      <entry>
+       <literal>&amp;&amp;</>
+       <literal>&amp;&lt;</>
+       <literal>&amp;&gt;</>
+       <literal>-|-</>
+       <literal>&lt;&lt;</>
+       <literal>&lt;@</>
+       <literal>=</>
+       <literal>&gt;&gt;</>
+       <literal>@&gt;</>
+      </entry>
+     </row>
+     <row>
+      <entry><literal>text_ops</></entry>
+      <entry><type>text</></entry>
+      <entry>
+       <literal>&lt;</>
+       <literal>&lt;=</>
+       <literal>=</>
+       <literal>&gt;</>
+       <literal>&gt;=</>
+       <literal>~&lt;=~</>
+       <literal>~&lt;~</>
+       <literal>~&gt;=~</>
+       <literal>~&gt;~</>
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+ <para>
+  Of the two operator classes for type <type>point</>,
+  <literal>quad_point_ops</> is the default.  <literal>kd_point_ops</>
+  supports the same operators but uses a different index data structure which
+  may offer better performance in some applications.
+ </para>
+
+</sect1>
+
 <sect1 id="spgist-extensibility">
  <title>Extensibility</title>
 
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 6b23069e26c..7b4391bba17 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -23,7 +23,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
 	geo_ops.o geo_selfuncs.o inet_cidr_ntop.o inet_net_pton.o int.o \
 	int8.o json.o jsonb.o jsonb_gin.o jsonb_op.o jsonb_util.o \
 	jsonfuncs.o like.o lockfuncs.o mac.o misc.o nabstime.o name.o \
-	network.o numeric.o numutils.o oid.o oracle_compat.o \
+	network.o network_gist.o network_selfuncs.o \
+	numeric.o numutils.o oid.o oracle_compat.o \
 	orderedsetaggs.o pg_lzcompress.o pg_locale.o pg_lsn.o \
 	pgstatfuncs.o pseudotypes.o quote.o rangetypes.o rangetypes_gist.o \
 	rangetypes_selfuncs.o rangetypes_spgist.o rangetypes_typanalyze.o \
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 5e837fab2de..8bdf5778d89 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -23,58 +23,9 @@
 
 
 static int32 network_cmp_internal(inet *a1, inet *a2);
-static int	bitncmp(void *l, void *r, int n);
 static bool addressOK(unsigned char *a, int bits, int family);
-static int	ip_addrsize(inet *inetptr);
 static inet *internal_inetpl(inet *ip, int64 addend);
 
-/*
- *	Access macros.	We use VARDATA_ANY so that we can process short-header
- *	varlena values without detoasting them.  This requires a trick:
- *	VARDATA_ANY assumes the varlena header is already filled in, which is
- *	not the case when constructing a new value (until SET_INET_VARSIZE is
- *	called, which we typically can't do till the end).  Therefore, we
- *	always initialize the newly-allocated value to zeroes (using palloc0).
- *	A zero length word will look like the not-1-byte case to VARDATA_ANY,
- *	and so we correctly construct an uncompressed value.
- *
- *	Note that ip_maxbits() and SET_INET_VARSIZE() require
- *	the family field to be set correctly.
- */
-
-#define ip_family(inetptr) \
-	(((inet_struct *) VARDATA_ANY(inetptr))->family)
-
-#define ip_bits(inetptr) \
-	(((inet_struct *) VARDATA_ANY(inetptr))->bits)
-
-#define ip_addr(inetptr) \
-	(((inet_struct *) VARDATA_ANY(inetptr))->ipaddr)
-
-#define ip_maxbits(inetptr) \
-	(ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
-
-#define SET_INET_VARSIZE(dst) \
-	SET_VARSIZE(dst, VARHDRSZ + offsetof(inet_struct, ipaddr) + \
-				ip_addrsize(dst))
-
-
-/*
- * Return the number of bytes of address storage needed for this data type.
- */
-static int
-ip_addrsize(inet *inetptr)
-{
-	switch (ip_family(inetptr))
-	{
-		case PGSQL_AF_INET:
-			return 4;
-		case PGSQL_AF_INET6:
-			return 16;
-		default:
-			return 0;
-	}
-}
 
 /*
  * Common INET/CIDR input routine
@@ -596,6 +547,21 @@ network_supeq(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(false);
 }
 
+Datum
+network_overlap(PG_FUNCTION_ARGS)
+{
+	inet	   *a1 = PG_GETARG_INET_PP(0);
+	inet	   *a2 = PG_GETARG_INET_PP(1);
+
+	if (ip_family(a1) == ip_family(a2))
+	{
+		PG_RETURN_BOOL(bitncmp(ip_addr(a1), ip_addr(a2),
+							   Min(ip_bits(a1), ip_bits(a2))) == 0);
+	}
+
+	PG_RETURN_BOOL(false);
+}
+
 /*
  * Extract data from a network datatype.
  */
@@ -962,10 +928,10 @@ convert_network_to_scalar(Datum value, Oid typid)
  * author:
  *		Paul Vixie (ISC), June 1996
  */
-static int
-bitncmp(void *l, void *r, int n)
+int
+bitncmp(const unsigned char *l, const unsigned char *r, int n)
 {
-	u_int		lb,
+	unsigned int lb,
 				rb;
 	int			x,
 				b;
@@ -975,8 +941,8 @@ bitncmp(void *l, void *r, int n)
 	if (x || (n % 8) == 0)
 		return x;
 
-	lb = ((const u_char *) l)[b];
-	rb = ((const u_char *) r)[b];
+	lb = l[b];
+	rb = r[b];
 	for (b = n % 8; b > 0; b--)
 	{
 		if (IS_HIGHBIT_SET(lb) != IS_HIGHBIT_SET(rb))
@@ -991,6 +957,49 @@ bitncmp(void *l, void *r, int n)
 	return 0;
 }
 
+/*
+ * bitncommon: compare bit masks l and r, for up to n bits.
+ *
+ * Returns the number of leading bits that match (0 to n).
+ */
+int
+bitncommon(const unsigned char *l, const unsigned char *r, int n)
+{
+	int			byte,
+				nbits;
+
+	/* number of bits to examine in last byte */
+	nbits = n % 8;
+
+	/* check whole bytes */
+	for (byte = 0; byte < n / 8; byte++)
+	{
+		if (l[byte] != r[byte])
+		{
+			/* at least one bit in the last byte is not common */
+			nbits = 7;
+			break;
+		}
+	}
+
+	/* check bits in last partial byte */
+	if (nbits != 0)
+	{
+		/* calculate diff of first non-matching bytes */
+		unsigned int diff = l[byte] ^ r[byte];
+
+		/* compare the bits from the most to the least */
+		while ((diff >> (8 - nbits)) != 0)
+			nbits--;
+	}
+
+	return (8 * byte) + nbits;
+}
+
+
+/*
+ * Verify a CIDR address is OK (doesn't have bits set past the masklen)
+ */
 static bool
 addressOK(unsigned char *a, int bits, int family)
 {
diff --git a/src/backend/utils/adt/network_gist.c b/src/backend/utils/adt/network_gist.c
new file mode 100644
index 00000000000..0a826ae90a2
--- /dev/null
+++ b/src/backend/utils/adt/network_gist.c
@@ -0,0 +1,789 @@
+/*-------------------------------------------------------------------------
+ *
+ * network_gist.c
+ *	  GiST support for network types.
+ *
+ * The key thing to understand about this code is the definition of the
+ * "union" of a set of INET/CIDR values.  It works like this:
+ * 1. If the values are not all of the same IP address family, the "union"
+ * is a dummy value with family number zero, minbits zero, commonbits zero,
+ * address all zeroes.	Otherwise:
+ * 2. The union has the common IP address family number.
+ * 3. The union's minbits value is the smallest netmask length ("ip_bits")
+ * of all the input values.
+ * 4. Let C be the number of leading address bits that are in common among
+ * all the input values (C ranges from 0 to ip_maxbits for the family).
+ * 5. The union's commonbits value is C.
+ * 6. The union's address value is the same as the common prefix for its
+ * first C bits, and is zeroes to the right of that.  The physical width
+ * of the address value is ip_maxbits for the address family.
+ *
+ * In a leaf index entry (representing a single key), commonbits is equal to
+ * ip_maxbits for the address family, minbits is the same as the represented
+ * value's ip_bits, and the address is equal to the represented address.
+ * Although it may appear that we're wasting a byte by storing the union
+ * format and not just the represented INET/CIDR value in leaf keys, the
+ * extra byte is actually "free" because of alignment considerations.
+ *
+ * Note that this design tracks minbits and commonbits independently; in any
+ * given union value, either might be smaller than the other.  This does not
+ * help us much when descending the tree, because of the way inet comparison
+ * is defined: at non-leaf nodes we can't compare more than minbits bits
+ * even if we know them.  However, it greatly improves the quality of split
+ * decisions.  Preliminary testing suggests that searches are as much as
+ * twice as fast as for a simpler design in which a single field doubles as
+ * the common prefix length and the minimum ip_bits value.
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/adt/network_gist.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <sys/socket.h>
+
+#include "access/gist.h"
+#include "access/skey.h"
+#include "utils/inet.h"
+
+/*
+ * Operator strategy numbers used in the GiST inet_ops opclass
+ */
+#define INETSTRAT_OVERLAPS		3
+#define INETSTRAT_EQ			18
+#define INETSTRAT_NE			19
+#define INETSTRAT_LT			20
+#define INETSTRAT_LE			21
+#define INETSTRAT_GT			22
+#define INETSTRAT_GE			23
+#define INETSTRAT_SUB			24
+#define INETSTRAT_SUBEQ			25
+#define INETSTRAT_SUP			26
+#define INETSTRAT_SUPEQ			27
+
+
+/*
+ * Representation of a GiST INET/CIDR index key.  This is not identical to
+ * INET/CIDR because we need to keep track of the length of the common address
+ * prefix as well as the minimum netmask length.  However, as long as it
+ * follows varlena header rules, the core GiST code won't know the difference.
+ * For simplicity we always use 1-byte-header varlena format.
+ */
+typedef struct GistInetKey
+{
+	uint8		va_header;		/* varlena header --- don't touch directly */
+	unsigned char family;		/* PGSQL_AF_INET, PGSQL_AF_INET6, or zero */
+	unsigned char minbits;		/* minimum number of bits in netmask */
+	unsigned char commonbits;	/* number of common prefix bits in addresses */
+	unsigned char ipaddr[16];	/* up to 128 bits of common address */
+} GistInetKey;
+
+#define DatumGetInetKeyP(X) ((GistInetKey *) DatumGetPointer(X))
+#define InetKeyPGetDatum(X) PointerGetDatum(X)
+
+/*
+ * Access macros; not really exciting, but we use these for notational
+ * consistency with access to INET/CIDR values.  Note that family-zero values
+ * are stored with 4 bytes of address, not 16.
+ */
+#define gk_ip_family(gkptr)		((gkptr)->family)
+#define gk_ip_minbits(gkptr)	((gkptr)->minbits)
+#define gk_ip_commonbits(gkptr) ((gkptr)->commonbits)
+#define gk_ip_addr(gkptr)		((gkptr)->ipaddr)
+#define ip_family_maxbits(fam)	((fam) == PGSQL_AF_INET6 ? 128 : 32)
+
+/* These require that the family field has been set: */
+#define gk_ip_addrsize(gkptr) \
+	(gk_ip_family(gkptr) == PGSQL_AF_INET6 ? 16 : 4)
+#define gk_ip_maxbits(gkptr) \
+	ip_family_maxbits(gk_ip_family(gkptr))
+#define SET_GK_VARSIZE(dst) \
+	SET_VARSIZE_SHORT(dst, offsetof(GistInetKey, ipaddr) + gk_ip_addrsize(dst))
+
+
+/*
+ * The GiST query consistency check
+ */
+Datum
+inet_gist_consistent(PG_FUNCTION_ARGS)
+{
+	GISTENTRY  *ent = (GISTENTRY *) PG_GETARG_POINTER(0);
+	inet	   *query = PG_GETARG_INET_PP(1);
+	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+
+	/* Oid		subtype = PG_GETARG_OID(3); */
+	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	GistInetKey *key = DatumGetInetKeyP(ent->key);
+	int			minbits,
+				order;
+
+	/* All operators served by this function are exact. */
+	*recheck = false;
+
+	/*
+	 * Check 0: different families
+	 *
+	 * If key represents multiple address families, its children could match
+	 * anything.  This can only happen on an inner index page.
+	 */
+	if (gk_ip_family(key) == 0)
+	{
+		Assert(!GIST_LEAF(ent));
+		PG_RETURN_BOOL(true);
+	}
+
+	/*
+	 * Check 1: different families
+	 *
+	 * Matching families do not help any of the strategies.
+	 */
+	if (gk_ip_family(key) != ip_family(query))
+	{
+		switch (strategy)
+		{
+			case INETSTRAT_LT:
+			case INETSTRAT_LE:
+				if (gk_ip_family(key) < ip_family(query))
+					PG_RETURN_BOOL(true);
+				break;
+
+			case INETSTRAT_GE:
+			case INETSTRAT_GT:
+				if (gk_ip_family(key) > ip_family(query))
+					PG_RETURN_BOOL(true);
+				break;
+
+			case INETSTRAT_NE:
+				PG_RETURN_BOOL(true);
+		}
+		/* For all other cases, we can be sure there is no match */
+		PG_RETURN_BOOL(false);
+	}
+
+	/*
+	 * Check 2: network bit count
+	 *
+	 * Network bit count (ip_bits) helps to check leaves for sub network and
+	 * sup network operators.  At non-leaf nodes, we know every child value
+	 * has ip_bits >= gk_ip_minbits(key), so we can avoid descending in some
+	 * cases too.
+	 */
+	switch (strategy)
+	{
+		case INETSTRAT_SUB:
+			if (GIST_LEAF(ent) && gk_ip_minbits(key) <= ip_bits(query))
+				PG_RETURN_BOOL(false);
+			break;
+
+		case INETSTRAT_SUBEQ:
+			if (GIST_LEAF(ent) && gk_ip_minbits(key) < ip_bits(query))
+				PG_RETURN_BOOL(false);
+			break;
+
+		case INETSTRAT_SUPEQ:
+		case INETSTRAT_EQ:
+			if (gk_ip_minbits(key) > ip_bits(query))
+				PG_RETURN_BOOL(false);
+			break;
+
+		case INETSTRAT_SUP:
+			if (gk_ip_minbits(key) >= ip_bits(query))
+				PG_RETURN_BOOL(false);
+			break;
+	}
+
+	/*
+	 * Check 3: common network bits
+	 *
+	 * Compare available common prefix bits to the query, but not beyond
+	 * either the query's netmask or the minimum netmask among the represented
+	 * values.	If these bits don't match the query, we have our answer (and
+	 * may or may not need to descend, depending on the operator).	If they do
+	 * match, and we are not at a leaf, we descend in all cases.
+	 *
+	 * Note this is the final check for operators that only consider the
+	 * network part of the address.
+	 */
+	minbits = Min(gk_ip_commonbits(key), gk_ip_minbits(key));
+	minbits = Min(minbits, ip_bits(query));
+
+	order = bitncmp(gk_ip_addr(key), ip_addr(query), minbits);
+
+	switch (strategy)
+	{
+		case INETSTRAT_SUB:
+		case INETSTRAT_SUBEQ:
+		case INETSTRAT_OVERLAPS:
+		case INETSTRAT_SUPEQ:
+		case INETSTRAT_SUP:
+			PG_RETURN_BOOL(order == 0);
+
+		case INETSTRAT_LT:
+		case INETSTRAT_LE:
+			if (order > 0)
+				PG_RETURN_BOOL(false);
+			if (order < 0 || !GIST_LEAF(ent))
+				PG_RETURN_BOOL(true);
+			break;
+
+		case INETSTRAT_EQ:
+			if (order != 0)
+				PG_RETURN_BOOL(false);
+			if (!GIST_LEAF(ent))
+				PG_RETURN_BOOL(true);
+			break;
+
+		case INETSTRAT_GE:
+		case INETSTRAT_GT:
+			if (order < 0)
+				PG_RETURN_BOOL(false);
+			if (order > 0 || !GIST_LEAF(ent))
+				PG_RETURN_BOOL(true);
+			break;
+
+		case INETSTRAT_NE:
+			if (order != 0 || !GIST_LEAF(ent))
+				PG_RETURN_BOOL(true);
+			break;
+	}
+
+	/*
+	 * Remaining checks are only for leaves and basic comparison strategies.
+	 * See network_cmp_internal() in network.c for the implementation we need
+	 * to match.  Note that in a leaf key, commonbits should equal the address
+	 * length, so we compared the whole network parts above.
+	 */
+	Assert(GIST_LEAF(ent));
+
+	/*
+	 * Check 4: network bit count
+	 *
+	 * Next step is to compare netmask widths.
+	 */
+	switch (strategy)
+	{
+		case INETSTRAT_LT:
+		case INETSTRAT_LE:
+			if (gk_ip_minbits(key) < ip_bits(query))
+				PG_RETURN_BOOL(true);
+			if (gk_ip_minbits(key) > ip_bits(query))
+				PG_RETURN_BOOL(false);
+			break;
+
+		case INETSTRAT_EQ:
+			if (gk_ip_minbits(key) != ip_bits(query))
+				PG_RETURN_BOOL(false);
+			break;
+
+		case INETSTRAT_GE:
+		case INETSTRAT_GT:
+			if (gk_ip_minbits(key) > ip_bits(query))
+				PG_RETURN_BOOL(true);
+			if (gk_ip_minbits(key) < ip_bits(query))
+				PG_RETURN_BOOL(false);
+			break;
+
+		case INETSTRAT_NE:
+			if (gk_ip_minbits(key) != ip_bits(query))
+				PG_RETURN_BOOL(true);
+			break;
+	}
+
+	/*
+	 * Check 5: whole address
+	 *
+	 * Netmask bit counts are the same, so check all the address bits.
+	 */
+	order = bitncmp(gk_ip_addr(key), ip_addr(query), gk_ip_maxbits(key));
+
+	switch (strategy)
+	{
+		case INETSTRAT_LT:
+			PG_RETURN_BOOL(order < 0);
+
+		case INETSTRAT_LE:
+			PG_RETURN_BOOL(order <= 0);
+
+		case INETSTRAT_EQ:
+			PG_RETURN_BOOL(order == 0);
+
+		case INETSTRAT_GE:
+			PG_RETURN_BOOL(order >= 0);
+
+		case INETSTRAT_GT:
+			PG_RETURN_BOOL(order > 0);
+
+		case INETSTRAT_NE:
+			PG_RETURN_BOOL(order != 0);
+	}
+
+	elog(ERROR, "unknown strategy for inet GiST");
+	PG_RETURN_BOOL(false);		/* keep compiler quiet */
+}
+
+/*
+ * Calculate parameters of the union of some GistInetKeys.
+ *
+ * Examine the keys in elements m..n inclusive of the GISTENTRY array,
+ * and compute these output parameters:
+ * *minfamily_p = minimum IP address family number
+ * *maxfamily_p = maximum IP address family number
+ * *minbits_p = minimum netmask width
+ * *commonbits_p = number of leading bits in common among the addresses
+ *
+ * minbits and commonbits are forced to zero if there's more than one
+ * address family.
+ */
+static void
+calc_inet_union_params(GISTENTRY *ent,
+					   int m, int n,
+					   int *minfamily_p,
+					   int *maxfamily_p,
+					   int *minbits_p,
+					   int *commonbits_p)
+{
+	int			minfamily,
+				maxfamily,
+				minbits,
+				commonbits;
+	unsigned char *addr;
+	GistInetKey *tmp;
+	int			i;
+
+	/* Must be at least one key. */
+	Assert(m <= n);
+
+	/* Initialize variables using the first key. */
+	tmp = DatumGetInetKeyP(ent[m].key);
+	minfamily = maxfamily = gk_ip_family(tmp);
+	minbits = gk_ip_minbits(tmp);
+	commonbits = gk_ip_commonbits(tmp);
+	addr = gk_ip_addr(tmp);
+
+	/* Scan remaining keys. */
+	for (i = m + 1; i <= n; i++)
+	{
+		tmp = DatumGetInetKeyP(ent[i].key);
+
+		/* Determine range of family numbers */
+		if (minfamily > gk_ip_family(tmp))
+			minfamily = gk_ip_family(tmp);
+		if (maxfamily < gk_ip_family(tmp))
+			maxfamily = gk_ip_family(tmp);
+
+		/* Find minimum minbits */
+		if (minbits > gk_ip_minbits(tmp))
+			minbits = gk_ip_minbits(tmp);
+
+		/* Find minimum number of bits in common */
+		if (commonbits > gk_ip_commonbits(tmp))
+			commonbits = gk_ip_commonbits(tmp);
+		if (commonbits > 0)
+			commonbits = bitncommon(addr, gk_ip_addr(tmp), commonbits);
+	}
+
+	/* Force minbits/commonbits to zero if more than one family. */
+	if (minfamily != maxfamily)
+		minbits = commonbits = 0;
+
+	*minfamily_p = minfamily;
+	*maxfamily_p = maxfamily;
+	*minbits_p = minbits;
+	*commonbits_p = commonbits;
+}
+
+/*
+ * Same as above, but the GISTENTRY elements to examine are those with
+ * indices listed in the offsets[] array.
+ */
+static void
+calc_inet_union_params_indexed(GISTENTRY *ent,
+							   OffsetNumber *offsets, int noffsets,
+							   int *minfamily_p,
+							   int *maxfamily_p,
+							   int *minbits_p,
+							   int *commonbits_p)
+{
+	int			minfamily,
+				maxfamily,
+				minbits,
+				commonbits;
+	unsigned char *addr;
+	GistInetKey *tmp;
+	int			i;
+
+	/* Must be at least one key. */
+	Assert(noffsets > 0);
+
+	/* Initialize variables using the first key. */
+	tmp = DatumGetInetKeyP(ent[offsets[0]].key);
+	minfamily = maxfamily = gk_ip_family(tmp);
+	minbits = gk_ip_minbits(tmp);
+	commonbits = gk_ip_commonbits(tmp);
+	addr = gk_ip_addr(tmp);
+
+	/* Scan remaining keys. */
+	for (i = 1; i < noffsets; i++)
+	{
+		tmp = DatumGetInetKeyP(ent[offsets[i]].key);
+
+		/* Determine range of family numbers */
+		if (minfamily > gk_ip_family(tmp))
+			minfamily = gk_ip_family(tmp);
+		if (maxfamily < gk_ip_family(tmp))
+			maxfamily = gk_ip_family(tmp);
+
+		/* Find minimum minbits */
+		if (minbits > gk_ip_minbits(tmp))
+			minbits = gk_ip_minbits(tmp);
+
+		/* Find minimum number of bits in common */
+		if (commonbits > gk_ip_commonbits(tmp))
+			commonbits = gk_ip_commonbits(tmp);
+		if (commonbits > 0)
+			commonbits = bitncommon(addr, gk_ip_addr(tmp), commonbits);
+	}
+
+	/* Force minbits/commonbits to zero if more than one family. */
+	if (minfamily != maxfamily)
+		minbits = commonbits = 0;
+
+	*minfamily_p = minfamily;
+	*maxfamily_p = maxfamily;
+	*minbits_p = minbits;
+	*commonbits_p = commonbits;
+}
+
+/*
+ * Construct a GistInetKey representing a union value.
+ *
+ * Inputs are the family/minbits/commonbits values to use, plus a pointer to
+ * the address field of one of the union inputs.  (Since we're going to copy
+ * just the bits-in-common, it doesn't matter which one.)
+ */
+static GistInetKey *
+build_inet_union_key(int family, int minbits, int commonbits,
+					 unsigned char *addr)
+{
+	GistInetKey *result;
+
+	/* Make sure any unused bits are zeroed. */
+	result = (GistInetKey *) palloc0(sizeof(GistInetKey));
+
+	gk_ip_family(result) = family;
+	gk_ip_minbits(result) = minbits;
+	gk_ip_commonbits(result) = commonbits;
+
+	/* Clone appropriate bytes of the address. */
+	if (commonbits > 0)
+		memcpy(gk_ip_addr(result), addr, (commonbits + 7) / 8);
+
+	/* Clean any unwanted bits in the last partial byte. */
+	if (commonbits % 8 != 0)
+		gk_ip_addr(result)[commonbits / 8] &= ~(0xFF >> (commonbits % 8));
+
+	/* Set varlena header correctly. */
+	SET_GK_VARSIZE(result);
+
+	return result;
+}
+
+
+/*
+ * The GiST union function
+ *
+ * See comments at head of file for the definition of the union.
+ */
+Datum
+inet_gist_union(PG_FUNCTION_ARGS)
+{
+	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+	GISTENTRY  *ent = entryvec->vector;
+	int			minfamily,
+				maxfamily,
+				minbits,
+				commonbits;
+	unsigned char *addr;
+	GistInetKey *tmp,
+			   *result;
+
+	/* Determine parameters of the union. */
+	calc_inet_union_params(ent, 0, entryvec->n - 1,
+						   &minfamily, &maxfamily,
+						   &minbits, &commonbits);
+
+	/* If more than one family, emit family number zero. */
+	if (minfamily != maxfamily)
+		minfamily = 0;
+
+	/* Initialize address using the first key. */
+	tmp = DatumGetInetKeyP(ent[0].key);
+	addr = gk_ip_addr(tmp);
+
+	/* Construct the union value. */
+	result = build_inet_union_key(minfamily, minbits, commonbits, addr);
+
+	PG_RETURN_POINTER(result);
+}
+
+/*
+ * The GiST compress function
+ *
+ * Convert an inet value to GistInetKey.
+ */
+Datum
+inet_gist_compress(PG_FUNCTION_ARGS)
+{
+	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	GISTENTRY  *retval;
+
+	if (entry->leafkey)
+	{
+		retval = palloc(sizeof(GISTENTRY));
+		if (DatumGetPointer(entry->key) != NULL)
+		{
+			inet	   *in = DatumGetInetPP(entry->key);
+			GistInetKey *r;
+
+			r = (GistInetKey *) palloc0(sizeof(GistInetKey));
+
+			gk_ip_family(r) = ip_family(in);
+			gk_ip_minbits(r) = ip_bits(in);
+			gk_ip_commonbits(r) = gk_ip_maxbits(r);
+			memcpy(gk_ip_addr(r), ip_addr(in), gk_ip_addrsize(r));
+			SET_GK_VARSIZE(r);
+
+			gistentryinit(*retval, PointerGetDatum(r),
+						  entry->rel, entry->page,
+						  entry->offset, FALSE);
+		}
+		else
+		{
+			gistentryinit(*retval, (Datum) 0,
+						  entry->rel, entry->page,
+						  entry->offset, FALSE);
+		}
+	}
+	else
+		retval = entry;
+	PG_RETURN_POINTER(retval);
+}
+
+/*
+ * The GiST decompress function
+ *
+ * do not do anything --- we just use the stored GistInetKey as-is.
+ */
+Datum
+inet_gist_decompress(PG_FUNCTION_ARGS)
+{
+	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+
+	PG_RETURN_POINTER(entry);
+}
+
+/*
+ * The GiST page split penalty function
+ *
+ * Charge a large penalty if address family doesn't match, or a somewhat
+ * smaller one if the new value would degrade the union's minbits
+ * (minimum netmask width).  Otherwise, penalty is inverse of the
+ * new number of common address bits.
+ */
+Datum
+inet_gist_penalty(PG_FUNCTION_ARGS)
+{
+	GISTENTRY  *origent = (GISTENTRY *) PG_GETARG_POINTER(0);
+	GISTENTRY  *newent = (GISTENTRY *) PG_GETARG_POINTER(1);
+	float	   *penalty = (float *) PG_GETARG_POINTER(2);
+	GistInetKey *orig = DatumGetInetKeyP(origent->key),
+			   *new = DatumGetInetKeyP(newent->key);
+	int			commonbits;
+
+	if (gk_ip_family(orig) == gk_ip_family(new))
+	{
+		if (gk_ip_minbits(orig) <= gk_ip_minbits(new))
+		{
+			commonbits = bitncommon(gk_ip_addr(orig), gk_ip_addr(new),
+									Min(gk_ip_commonbits(orig),
+										gk_ip_commonbits(new)));
+			if (commonbits > 0)
+				*penalty = 1.0f / commonbits;
+			else
+				*penalty = 2;
+		}
+		else
+			*penalty = 3;
+	}
+	else
+		*penalty = 4;
+
+	PG_RETURN_POINTER(penalty);
+}
+
+/*
+ * The GiST PickSplit method
+ *
+ * There are two ways to split. First one is to split by address families,
+ * if there are multiple families appearing in the input.
+ *
+ * The second and more common way is to split by addresses. To achieve this,
+ * determine the number of leading bits shared by all the keys, then split on
+ * the next bit.  (We don't currently consider the netmask widths while doing
+ * this; should we?)  If we fail to get a nontrivial split that way, split
+ * 50-50.
+ */
+Datum
+inet_gist_picksplit(PG_FUNCTION_ARGS)
+{
+	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+	GIST_SPLITVEC *splitvec = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	GISTENTRY  *ent = entryvec->vector;
+	int			minfamily,
+				maxfamily,
+				minbits,
+				commonbits;
+	unsigned char *addr;
+	GistInetKey *tmp,
+			   *left_union,
+			   *right_union;
+	int			maxoff,
+				nbytes;
+	OffsetNumber i,
+			   *left,
+			   *right;
+
+	maxoff = entryvec->n - 1;
+	nbytes = (maxoff + 1) * sizeof(OffsetNumber);
+
+	left = (OffsetNumber *) palloc(nbytes);
+	right = (OffsetNumber *) palloc(nbytes);
+
+	splitvec->spl_left = left;
+	splitvec->spl_right = right;
+
+	splitvec->spl_nleft = 0;
+	splitvec->spl_nright = 0;
+
+	/* Determine parameters of the union of all the inputs. */
+	calc_inet_union_params(ent, FirstOffsetNumber, maxoff,
+						   &minfamily, &maxfamily,
+						   &minbits, &commonbits);
+
+	if (minfamily != maxfamily)
+	{
+		/* Multiple families, so split by family. */
+		for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+		{
+			/*
+			 * If there's more than 2 families, all but maxfamily go into the
+			 * left union.	This could only happen if the inputs include some
+			 * IPv4, some IPv6, and some already-multiple-family unions.
+			 */
+			tmp = DatumGetInetKeyP(ent[i].key);
+			if (gk_ip_family(tmp) != maxfamily)
+				left[splitvec->spl_nleft++] = i;
+			else
+				right[splitvec->spl_nright++] = i;
+		}
+	}
+	else
+	{
+		/*
+		 * Split on the next bit after the common bits.  If that yields a
+		 * trivial split, try the next bit position to the right.  Repeat till
+		 * success; or if we run out of bits, do an arbitrary 50-50 split.
+		 */
+		int			maxbits = ip_family_maxbits(minfamily);
+
+		while (commonbits < maxbits)
+		{
+			/* Split using the commonbits'th bit position. */
+			int			bitbyte = commonbits / 8;
+			int			bitmask = 0x80 >> (commonbits % 8);
+
+			splitvec->spl_nleft = splitvec->spl_nright = 0;
+
+			for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+			{
+				tmp = DatumGetInetKeyP(ent[i].key);
+				addr = gk_ip_addr(tmp);
+				if ((addr[bitbyte] & bitmask) == 0)
+					left[splitvec->spl_nleft++] = i;
+				else
+					right[splitvec->spl_nright++] = i;
+			}
+
+			if (splitvec->spl_nleft > 0 && splitvec->spl_nright > 0)
+				break;			/* success */
+			commonbits++;
+		}
+
+		if (commonbits >= maxbits)
+		{
+			/* Failed ... do a 50-50 split. */
+			splitvec->spl_nleft = splitvec->spl_nright = 0;
+
+			for (i = FirstOffsetNumber; i <= maxoff / 2; i = OffsetNumberNext(i))
+			{
+				left[splitvec->spl_nleft++] = i;
+			}
+			for (; i <= maxoff; i = OffsetNumberNext(i))
+			{
+				right[splitvec->spl_nright++] = i;
+			}
+		}
+	}
+
+	/*
+	 * Compute the union value for each side from scratch.	In most cases we
+	 * could approximate the union values with what we already know, but this
+	 * ensures that each side has minbits and commonbits set as high as
+	 * possible.
+	 */
+	calc_inet_union_params_indexed(ent, left, splitvec->spl_nleft,
+								   &minfamily, &maxfamily,
+								   &minbits, &commonbits);
+	if (minfamily != maxfamily)
+		minfamily = 0;
+	tmp = DatumGetInetKeyP(ent[left[0]].key);
+	addr = gk_ip_addr(tmp);
+	left_union = build_inet_union_key(minfamily, minbits, commonbits, addr);
+	splitvec->spl_ldatum = PointerGetDatum(left_union);
+
+	calc_inet_union_params_indexed(ent, right, splitvec->spl_nright,
+								   &minfamily, &maxfamily,
+								   &minbits, &commonbits);
+	if (minfamily != maxfamily)
+		minfamily = 0;
+	tmp = DatumGetInetKeyP(ent[right[0]].key);
+	addr = gk_ip_addr(tmp);
+	right_union = build_inet_union_key(minfamily, minbits, commonbits, addr);
+	splitvec->spl_rdatum = PointerGetDatum(right_union);
+
+	PG_RETURN_POINTER(splitvec);
+}
+
+/*
+ * The GiST equality function
+ */
+Datum
+inet_gist_same(PG_FUNCTION_ARGS)
+{
+	GistInetKey *left = DatumGetInetKeyP(PG_GETARG_DATUM(0));
+	GistInetKey *right = DatumGetInetKeyP(PG_GETARG_DATUM(1));
+	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+
+	*result = (gk_ip_family(left) == gk_ip_family(right) &&
+			   gk_ip_minbits(left) == gk_ip_minbits(right) &&
+			   gk_ip_commonbits(left) == gk_ip_commonbits(right) &&
+			   memcmp(gk_ip_addr(left), gk_ip_addr(right),
+					  gk_ip_addrsize(left)) == 0);
+
+	PG_RETURN_POINTER(result);
+}
diff --git a/src/backend/utils/adt/network_selfuncs.c b/src/backend/utils/adt/network_selfuncs.c
new file mode 100644
index 00000000000..d0d806f889d
--- /dev/null
+++ b/src/backend/utils/adt/network_selfuncs.c
@@ -0,0 +1,32 @@
+/*-------------------------------------------------------------------------
+ *
+ * network_selfuncs.c
+ *	  Functions for selectivity estimation of inet/cidr operators
+ *
+ * Currently these are just stubs, but we hope to do better soon.
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/adt/network_selfuncs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "utils/inet.h"
+
+
+Datum
+networksel(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_FLOAT8(0.001);
+}
+
+Datum
+networkjoinsel(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_FLOAT8(0.001);
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 4b3357ccd97..fe6144e2d34 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201404081
+#define CATALOG_VERSION_NO	201404082
 
 #endif
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index 26231135570..8efd3be3c66 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -818,4 +818,19 @@ DATA(insert (	3474   3831 3831 8 s	3892 4000 0 ));
 DATA(insert (	3474   3831 2283 16 s	3889 4000 0 ));
 DATA(insert (	3474   3831 3831 18 s	3882 4000 0 ));
 
+/*
+ * GiST inet_ops
+ */
+DATA(insert (	3550	869 869 3 s		3552 783 0 ));
+DATA(insert (	3550	869 869 18 s	1201 783 0 ));
+DATA(insert (	3550	869 869 19 s	1202 783 0 ));
+DATA(insert (	3550	869 869 20 s	1203 783 0 ));
+DATA(insert (	3550	869 869 21 s	1204 783 0 ));
+DATA(insert (	3550	869 869 22 s	1205 783 0 ));
+DATA(insert (	3550	869 869 23 s	1206 783 0 ));
+DATA(insert (	3550	869 869 24 s	931 783 0 ));
+DATA(insert (	3550	869 869 25 s	932 783 0 ));
+DATA(insert (	3550	869 869 26 s	933 783 0 ));
+DATA(insert (	3550	869 869 27 s	934 783 0 ));
+
 #endif   /* PG_AMOP_H */
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index b28dd563a8c..198b126964f 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -399,6 +399,13 @@ DATA(insert (	4037   3802 3802 2 3485 ));
 DATA(insert (	4037   3802 3802 3 3486 ));
 DATA(insert (	4037   3802 3802 4 3487 ));
 DATA(insert (	4037   3802 3802 6 3489 ));
+DATA(insert (	3550   869	869  1 3553 ));
+DATA(insert (	3550   869	869  2 3554 ));
+DATA(insert (	3550   869	869  3 3555 ));
+DATA(insert (	3550   869	869  4 3556 ));
+DATA(insert (	3550   869	869  5 3557 ));
+DATA(insert (	3550   869	869  6 3558 ));
+DATA(insert (	3550   869	869  7 3559 ));
 
 /* sp-gist */
 DATA(insert (	3474   3831 3831 1 3469 ));
diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h
index 63a40a8412c..49b24108de9 100644
--- a/src/include/catalog/pg_opclass.h
+++ b/src/include/catalog/pg_opclass.h
@@ -112,6 +112,7 @@ DATA(insert OID = 3123 ( 403	float8_ops	PGNSP PGUID 1970  701 t 0 ));
 DATA(insert (	405		float8_ops			PGNSP PGUID 1971  701 t 0 ));
 DATA(insert (	403		inet_ops			PGNSP PGUID 1974  869 t 0 ));
 DATA(insert (	405		inet_ops			PGNSP PGUID 1975  869 t 0 ));
+DATA(insert (	783		inet_ops			PGNSP PGUID 3550  869 f 0 ));
 DATA(insert OID = 1979 ( 403	int2_ops	PGNSP PGUID 1976   21 t 0 ));
 #define INT2_BTREE_OPS_OID 1979
 DATA(insert (	405		int2_ops			PGNSP PGUID 1977   21 t 0 ));
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index ac09034f3d0..f280af441c2 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1140,18 +1140,20 @@ DATA(insert OID = 1205 (  ">"	   PGNSP PGUID b f f 869 869	 16 1203 1204 network
 DESCR("greater than");
 DATA(insert OID = 1206 (  ">="	   PGNSP PGUID b f f 869 869	 16 1204 1203 network_ge scalargtsel scalargtjoinsel ));
 DESCR("greater than or equal");
-DATA(insert OID = 931  (  "<<"	   PGNSP PGUID b f f 869 869	 16 933		0 network_sub - - ));
+DATA(insert OID = 931  (  "<<"	   PGNSP PGUID b f f 869 869	 16 933		0 network_sub networksel networkjoinsel ));
 DESCR("is subnet");
 #define OID_INET_SUB_OP				  931
-DATA(insert OID = 932  (  "<<="    PGNSP PGUID b f f 869 869	 16 934		0 network_subeq - - ));
+DATA(insert OID = 932  (  "<<="    PGNSP PGUID b f f 869 869	 16 934		0 network_subeq networksel networkjoinsel ));
 DESCR("is subnet or equal");
 #define OID_INET_SUBEQ_OP				932
-DATA(insert OID = 933  (  ">>"	   PGNSP PGUID b f f 869 869	 16 931		0 network_sup - - ));
+DATA(insert OID = 933  (  ">>"	   PGNSP PGUID b f f 869 869	 16 931		0 network_sup networksel networkjoinsel ));
 DESCR("is supernet");
 #define OID_INET_SUP_OP				  933
-DATA(insert OID = 934  (  ">>="    PGNSP PGUID b f f 869 869	 16 932		0 network_supeq - - ));
+DATA(insert OID = 934  (  ">>="    PGNSP PGUID b f f 869 869	 16 932		0 network_supeq networksel networkjoinsel ));
 DESCR("is supernet or equal");
 #define OID_INET_SUPEQ_OP				934
+DATA(insert OID = 3552	(  "&&"    PGNSP PGUID b f f 869 869	 16 3552	0 network_overlap networksel networkjoinsel ));
+DESCR("overlaps (is subnet or supernet)");
 
 DATA(insert OID = 2634 (  "~"	   PGNSP PGUID l f f	  0 869 869 0 0 inetnot - - ));
 DESCR("bitwise not");
diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h
index 775be86c1af..9e8f4ac5b63 100644
--- a/src/include/catalog/pg_opfamily.h
+++ b/src/include/catalog/pg_opfamily.h
@@ -78,6 +78,7 @@ DATA(insert OID = 1971 (	405		float_ops		PGNSP PGUID ));
 DATA(insert OID = 1974 (	403		network_ops		PGNSP PGUID ));
 #define NETWORK_BTREE_FAM_OID 1974
 DATA(insert OID = 1975 (	405		network_ops		PGNSP PGUID ));
+DATA(insert OID = 3550 (	783		network_ops		PGNSP PGUID ));
 DATA(insert OID = 1976 (	403		integer_ops		PGNSP PGUID ));
 #define INTEGER_BTREE_FAM_OID 1976
 DATA(insert OID = 1977 (	405		integer_ops		PGNSP PGUID ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 21c17a08ed4..7b9c5870fac 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2120,6 +2120,7 @@ DATA(insert OID = 927 (  network_sub		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1
 DATA(insert OID = 928 (  network_subeq		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "869 869" _null_ _null_ _null_ _null_	network_subeq _null_ _null_ _null_ ));
 DATA(insert OID = 929 (  network_sup		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "869 869" _null_ _null_ _null_ _null_	network_sup _null_ _null_ _null_ ));
 DATA(insert OID = 930 (  network_supeq		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "869 869" _null_ _null_ _null_ _null_	network_supeq _null_ _null_ _null_ ));
+DATA(insert OID = 3551 (  network_overlap	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "869 869" _null_ _null_ _null_ _null_	network_overlap _null_ _null_ _null_ ));
 
 /* inet/cidr functions */
 DATA(insert OID = 598 (  abbrev				PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "869" _null_ _null_ _null_ _null_	inet_abbrev _null_ _null_ _null_ ));
@@ -2166,6 +2167,28 @@ DATA(insert OID = 2631 (  int8pl_inet		PGNSP PGUID 14 1 0 0 0 f f f f t f i 2 0
 DATA(insert OID = 2632 (  inetmi_int8		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 869 "869 20" _null_ _null_ _null_ _null_	inetmi_int8 _null_ _null_ _null_ ));
 DATA(insert OID = 2633 (  inetmi			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 20 "869 869" _null_ _null_ _null_ _null_	inetmi _null_ _null_ _null_ ));
 
+/* GiST support for inet and cidr */
+DATA(insert OID = 3553 (  inet_gist_consistent	PGNSP PGUID 12 1 0 0 0 f f f f t f i 5 0 16 "2281 869 23 26 2281" _null_ _null_ _null_ _null_ inet_gist_consistent _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3554 (  inet_gist_union		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ inet_gist_union _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3555 (  inet_gist_compress	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ inet_gist_compress _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3556 (  inet_gist_decompress	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ inet_gist_decompress _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3557 (  inet_gist_penalty		PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ inet_gist_penalty _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3558 (  inet_gist_picksplit	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ inet_gist_picksplit _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3559 (  inet_gist_same		PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 2281 "869 869 2281" _null_ _null_ _null_ _null_ inet_gist_same _null_ _null_ _null_ ));
+DESCR("GiST support");
+
+/* Selectivity estimation for inet and cidr */
+DATA(insert OID = 3560 (  networksel		   PGNSP PGUID 12 1 0 0 0 f f f f t f s 4 0 701 "2281 26 2281 23" _null_ _null_ _null_ _null_	networksel _null_ _null_ _null_ ));
+DESCR("restriction selectivity for network operators");
+DATA(insert OID = 3561 (  networkjoinsel	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 5 0 701 "2281 26 2281 21 2281" _null_ _null_ _null_ _null_	networkjoinsel _null_ _null_ _null_ ));
+DESCR("join selectivity for network operators");
+
 DATA(insert OID = 1690 ( time_mi_time		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1186 "1083 1083" _null_ _null_ _null_ _null_	time_mi_time _null_ _null_ _null_ ));
 
 DATA(insert OID = 1691 (  boolle			PGNSP PGUID 12 1 0 0 0 f f f t t f i 2 0 16 "16 16" _null_ _null_ _null_ _null_ boolle _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 720c8318011..5907cb13fd3 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -906,6 +906,7 @@ extern Datum network_sub(PG_FUNCTION_ARGS);
 extern Datum network_subeq(PG_FUNCTION_ARGS);
 extern Datum network_sup(PG_FUNCTION_ARGS);
 extern Datum network_supeq(PG_FUNCTION_ARGS);
+extern Datum network_overlap(PG_FUNCTION_ARGS);
 extern Datum network_network(PG_FUNCTION_ARGS);
 extern Datum network_netmask(PG_FUNCTION_ARGS);
 extern Datum network_hostmask(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index 330f32de8f1..bd31c7169a1 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -53,6 +53,38 @@ typedef struct
 	inet_struct inet_data;
 } inet;
 
+/*
+ *	Access macros.	We use VARDATA_ANY so that we can process short-header
+ *	varlena values without detoasting them.  This requires a trick:
+ *	VARDATA_ANY assumes the varlena header is already filled in, which is
+ *	not the case when constructing a new value (until SET_INET_VARSIZE is
+ *	called, which we typically can't do till the end).  Therefore, we
+ *	always initialize the newly-allocated value to zeroes (using palloc0).
+ *	A zero length word will look like the not-1-byte case to VARDATA_ANY,
+ *	and so we correctly construct an uncompressed value.
+ *
+ *	Note that ip_addrsize(), ip_maxbits(), and SET_INET_VARSIZE() require
+ *	the family field to be set correctly.
+ */
+#define ip_family(inetptr) \
+	(((inet_struct *) VARDATA_ANY(inetptr))->family)
+
+#define ip_bits(inetptr) \
+	(((inet_struct *) VARDATA_ANY(inetptr))->bits)
+
+#define ip_addr(inetptr) \
+	(((inet_struct *) VARDATA_ANY(inetptr))->ipaddr)
+
+#define ip_addrsize(inetptr) \
+	(ip_family(inetptr) == PGSQL_AF_INET ? 4 : 16)
+
+#define ip_maxbits(inetptr) \
+	(ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
+
+#define SET_INET_VARSIZE(dst) \
+	SET_VARSIZE(dst, VARHDRSZ + offsetof(inet_struct, ipaddr) + \
+				ip_addrsize(dst))
+
 
 /*
  *	This is the internal storage format for MAC addresses:
@@ -82,4 +114,27 @@ typedef struct macaddr
 #define PG_GETARG_MACADDR_P(n) DatumGetMacaddrP(PG_GETARG_DATUM(n))
 #define PG_RETURN_MACADDR_P(x) return MacaddrPGetDatum(x)
 
+/*
+ * Support functions in network.c
+ */
+extern int	bitncmp(const unsigned char *l, const unsigned char *r, int n);
+extern int	bitncommon(const unsigned char *l, const unsigned char *r, int n);
+
+/*
+ * GiST support functions in network_gist.c
+ */
+extern Datum inet_gist_consistent(PG_FUNCTION_ARGS);
+extern Datum inet_gist_union(PG_FUNCTION_ARGS);
+extern Datum inet_gist_compress(PG_FUNCTION_ARGS);
+extern Datum inet_gist_decompress(PG_FUNCTION_ARGS);
+extern Datum inet_gist_penalty(PG_FUNCTION_ARGS);
+extern Datum inet_gist_picksplit(PG_FUNCTION_ARGS);
+extern Datum inet_gist_same(PG_FUNCTION_ARGS);
+
+/*
+ * Estimation functions in network_selfuncs.c
+ */
+extern Datum networksel(PG_FUNCTION_ARGS);
+extern Datum networkjoinsel(PG_FUNCTION_ARGS);
+
 #endif   /* INET_H */
diff --git a/src/test/regress/expected/inet.out b/src/test/regress/expected/inet.out
index 356a397822d..008cc0b5ddf 100644
--- a/src/test/regress/expected/inet.out
+++ b/src/test/regress/expected/inet.out
@@ -180,27 +180,28 @@ SELECT '' AS ten, i, c,
   i < c AS lt, i <= c AS le, i = c AS eq,
   i >= c AS ge, i > c AS gt, i <> c AS ne,
   i << c AS sb, i <<= c AS sbe,
-  i >> c AS sup, i >>= c AS spe
+  i >> c AS sup, i >>= c AS spe,
+  i && c AS ovr
   FROM INET_TBL;
- ten |        i         |         c          | lt | le | eq | ge | gt | ne | sb | sbe | sup | spe 
------+------------------+--------------------+----+----+----+----+----+----+----+-----+-----+-----
-     | 192.168.1.226/24 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 192.168.1.226    | 192.168.1.0/26     | f  | f  | f  | t  | t  | t  | f  | f   | f   | f
-     | 192.168.1.0/24   | 192.168.1.0/24     | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
-     | 192.168.1.0/25   | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
-     | 192.168.1.255/24 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 192.168.1.255/25 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
-     | 10.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 10.1.2.3/8       | 10.0.0.0/32        | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
-     | 10.1.2.3         | 10.1.2.3/32        | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
-     | 10.1.2.3/24      | 10.1.2.0/24        | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 10.1.2.3/16      | 10.1.0.0/16        | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 10.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 11.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | f   | f   | f
-     | 9.1.2.3/8        | 10.0.0.0/8         | t  | t  | f  | f  | f  | t  | f  | f   | f   | f
-     | 10:23::f1/64     | 10:23::f1/128      | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
-     | 10:23::ffff      | 10:23::8000/113    | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
-     | ::4.3.2.1/24     | ::ffff:1.2.3.4/128 | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
+ ten |        i         |         c          | lt | le | eq | ge | gt | ne | sb | sbe | sup | spe | ovr 
+-----+------------------+--------------------+----+----+----+----+----+----+----+-----+-----+-----+-----
+     | 192.168.1.226/24 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t   | t
+     | 192.168.1.226    | 192.168.1.0/26     | f  | f  | f  | t  | t  | t  | f  | f   | f   | f   | f
+     | 192.168.1.0/24   | 192.168.1.0/24     | f  | t  | t  | t  | f  | f  | f  | t   | f   | t   | t
+     | 192.168.1.0/25   | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f   | t
+     | 192.168.1.255/24 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t   | t
+     | 192.168.1.255/25 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f   | t
+     | 10.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | t   | f   | t   | t
+     | 10.1.2.3/8       | 10.0.0.0/32        | t  | t  | f  | f  | f  | t  | f  | f   | t   | t   | t
+     | 10.1.2.3         | 10.1.2.3/32        | f  | t  | t  | t  | f  | f  | f  | t   | f   | t   | t
+     | 10.1.2.3/24      | 10.1.2.0/24        | f  | f  | f  | t  | t  | t  | f  | t   | f   | t   | t
+     | 10.1.2.3/16      | 10.1.0.0/16        | f  | f  | f  | t  | t  | t  | f  | t   | f   | t   | t
+     | 10.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | t   | f   | t   | t
+     | 11.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | f   | f   | f   | f
+     | 9.1.2.3/8        | 10.0.0.0/8         | t  | t  | f  | f  | f  | t  | f  | f   | f   | f   | f
+     | 10:23::f1/64     | 10:23::f1/128      | t  | t  | f  | f  | f  | t  | f  | f   | t   | t   | t
+     | 10:23::ffff      | 10:23::8000/113    | f  | f  | f  | t  | t  | t  | t  | t   | f   | f   | t
+     | ::4.3.2.1/24     | ::ffff:1.2.3.4/128 | t  | t  | f  | f  | f  | t  | f  | f   | t   | t   | t
 (17 rows)
 
 -- check the conversion to/from text and set_netmask
@@ -226,7 +227,7 @@ SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL;
      | ::4.3.2.1/24
 (17 rows)
 
--- check that index works correctly
+-- check that btree index works correctly
 CREATE INDEX inet_idx1 ON inet_tbl(i);
 SET enable_seqscan TO off;
 SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr;
@@ -250,6 +251,135 @@ SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr;
 
 SET enable_seqscan TO on;
 DROP INDEX inet_idx1;
+-- check that gist index works correctly
+CREATE INDEX inet_idx2 ON inet_tbl using gist (i inet_ops);
+SET enable_seqscan TO off;
+SELECT * FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i;
+       c        |        i         
+----------------+------------------
+ 192.168.1.0/24 | 192.168.1.0/25
+ 192.168.1.0/24 | 192.168.1.255/25
+ 192.168.1.0/26 | 192.168.1.226
+(3 rows)
+
+SELECT * FROM inet_tbl WHERE i <<= '192.168.1.0/24'::cidr ORDER BY i;
+       c        |        i         
+----------------+------------------
+ 192.168.1.0/24 | 192.168.1.0/24
+ 192.168.1.0/24 | 192.168.1.226/24
+ 192.168.1.0/24 | 192.168.1.255/24
+ 192.168.1.0/24 | 192.168.1.0/25
+ 192.168.1.0/24 | 192.168.1.255/25
+ 192.168.1.0/26 | 192.168.1.226
+(6 rows)
+
+SELECT * FROM inet_tbl WHERE i && '192.168.1.0/24'::cidr ORDER BY i;
+       c        |        i         
+----------------+------------------
+ 192.168.1.0/24 | 192.168.1.0/24
+ 192.168.1.0/24 | 192.168.1.226/24
+ 192.168.1.0/24 | 192.168.1.255/24
+ 192.168.1.0/24 | 192.168.1.0/25
+ 192.168.1.0/24 | 192.168.1.255/25
+ 192.168.1.0/26 | 192.168.1.226
+(6 rows)
+
+SELECT * FROM inet_tbl WHERE i >>= '192.168.1.0/24'::cidr ORDER BY i;
+       c        |        i         
+----------------+------------------
+ 192.168.1.0/24 | 192.168.1.0/24
+ 192.168.1.0/24 | 192.168.1.226/24
+ 192.168.1.0/24 | 192.168.1.255/24
+(3 rows)
+
+SELECT * FROM inet_tbl WHERE i >> '192.168.1.0/24'::cidr ORDER BY i;
+ c | i 
+---+---
+(0 rows)
+
+SELECT * FROM inet_tbl WHERE i < '192.168.1.0/24'::cidr ORDER BY i;
+      c      |      i      
+-------------+-------------
+ 10.0.0.0/8  | 9.1.2.3/8
+ 10.0.0.0/32 | 10.1.2.3/8
+ 10.0.0.0/8  | 10.1.2.3/8
+ 10.0.0.0/8  | 10.1.2.3/8
+ 10.1.0.0/16 | 10.1.2.3/16
+ 10.1.2.0/24 | 10.1.2.3/24
+ 10.1.2.3/32 | 10.1.2.3
+ 10.0.0.0/8  | 11.1.2.3/8
+(8 rows)
+
+SELECT * FROM inet_tbl WHERE i <= '192.168.1.0/24'::cidr ORDER BY i;
+       c        |       i        
+----------------+----------------
+ 10.0.0.0/8     | 9.1.2.3/8
+ 10.0.0.0/8     | 10.1.2.3/8
+ 10.0.0.0/32    | 10.1.2.3/8
+ 10.0.0.0/8     | 10.1.2.3/8
+ 10.1.0.0/16    | 10.1.2.3/16
+ 10.1.2.0/24    | 10.1.2.3/24
+ 10.1.2.3/32    | 10.1.2.3
+ 10.0.0.0/8     | 11.1.2.3/8
+ 192.168.1.0/24 | 192.168.1.0/24
+(9 rows)
+
+SELECT * FROM inet_tbl WHERE i = '192.168.1.0/24'::cidr ORDER BY i;
+       c        |       i        
+----------------+----------------
+ 192.168.1.0/24 | 192.168.1.0/24
+(1 row)
+
+SELECT * FROM inet_tbl WHERE i >= '192.168.1.0/24'::cidr ORDER BY i;
+         c          |        i         
+--------------------+------------------
+ 192.168.1.0/24     | 192.168.1.0/24
+ 192.168.1.0/24     | 192.168.1.226/24
+ 192.168.1.0/24     | 192.168.1.255/24
+ 192.168.1.0/24     | 192.168.1.0/25
+ 192.168.1.0/24     | 192.168.1.255/25
+ 192.168.1.0/26     | 192.168.1.226
+ ::ffff:1.2.3.4/128 | ::4.3.2.1/24
+ 10:23::f1/128      | 10:23::f1/64
+ 10:23::8000/113    | 10:23::ffff
+(9 rows)
+
+SELECT * FROM inet_tbl WHERE i > '192.168.1.0/24'::cidr ORDER BY i;
+         c          |        i         
+--------------------+------------------
+ 192.168.1.0/24     | 192.168.1.226/24
+ 192.168.1.0/24     | 192.168.1.255/24
+ 192.168.1.0/24     | 192.168.1.0/25
+ 192.168.1.0/24     | 192.168.1.255/25
+ 192.168.1.0/26     | 192.168.1.226
+ ::ffff:1.2.3.4/128 | ::4.3.2.1/24
+ 10:23::f1/128      | 10:23::f1/64
+ 10:23::8000/113    | 10:23::ffff
+(8 rows)
+
+SELECT * FROM inet_tbl WHERE i <> '192.168.1.0/24'::cidr ORDER BY i;
+         c          |        i         
+--------------------+------------------
+ 10.0.0.0/8         | 9.1.2.3/8
+ 10.0.0.0/8         | 10.1.2.3/8
+ 10.0.0.0/32        | 10.1.2.3/8
+ 10.0.0.0/8         | 10.1.2.3/8
+ 10.1.0.0/16        | 10.1.2.3/16
+ 10.1.2.0/24        | 10.1.2.3/24
+ 10.1.2.3/32        | 10.1.2.3
+ 10.0.0.0/8         | 11.1.2.3/8
+ 192.168.1.0/24     | 192.168.1.226/24
+ 192.168.1.0/24     | 192.168.1.255/24
+ 192.168.1.0/24     | 192.168.1.0/25
+ 192.168.1.0/24     | 192.168.1.255/25
+ 192.168.1.0/26     | 192.168.1.226
+ ::ffff:1.2.3.4/128 | ::4.3.2.1/24
+ 10:23::f1/128      | 10:23::f1/64
+ 10:23::8000/113    | 10:23::ffff
+(16 rows)
+
+SET enable_seqscan TO on;
+DROP INDEX inet_idx2;
 -- simple tests of inet boolean and arithmetic operators
 SELECT i, ~i AS "~i" FROM inet_tbl;
         i         |                     ~i                     
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index bf765014357..118f7e43dc0 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1118,6 +1118,15 @@ ORDER BY 1, 2, 3;
         783 |           15 | <->
         783 |           16 | @>
         783 |           18 | =
+        783 |           19 | <>
+        783 |           20 | <
+        783 |           21 | <=
+        783 |           22 | >
+        783 |           23 | >=
+        783 |           24 | <<
+        783 |           25 | <<=
+        783 |           26 | >>
+        783 |           27 | >>=
         783 |           28 | <@
         783 |           48 | <@
         783 |           68 | <@
@@ -1153,7 +1162,7 @@ ORDER BY 1, 2, 3;
        4000 |           15 | >
        4000 |           16 | @>
        4000 |           18 | =
-(71 rows)
+(80 rows)
 
 -- Check that all opclass search operators have selectivity estimators.
 -- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/sql/inet.sql b/src/test/regress/sql/inet.sql
index 328f14907ba..be078fbb847 100644
--- a/src/test/regress/sql/inet.sql
+++ b/src/test/regress/sql/inet.sql
@@ -52,12 +52,14 @@ SELECT '' AS ten, i, c,
   i < c AS lt, i <= c AS le, i = c AS eq,
   i >= c AS ge, i > c AS gt, i <> c AS ne,
   i << c AS sb, i <<= c AS sbe,
-  i >> c AS sup, i >>= c AS spe
+  i >> c AS sup, i >>= c AS spe,
+  i && c AS ovr
   FROM INET_TBL;
 
 -- check the conversion to/from text and set_netmask
 SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL;
--- check that index works correctly
+
+-- check that btree index works correctly
 CREATE INDEX inet_idx1 ON inet_tbl(i);
 SET enable_seqscan TO off;
 SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr;
@@ -65,6 +67,23 @@ SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr;
 SET enable_seqscan TO on;
 DROP INDEX inet_idx1;
 
+-- check that gist index works correctly
+CREATE INDEX inet_idx2 ON inet_tbl using gist (i inet_ops);
+SET enable_seqscan TO off;
+SELECT * FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i;
+SELECT * FROM inet_tbl WHERE i <<= '192.168.1.0/24'::cidr ORDER BY i;
+SELECT * FROM inet_tbl WHERE i && '192.168.1.0/24'::cidr ORDER BY i;
+SELECT * FROM inet_tbl WHERE i >>= '192.168.1.0/24'::cidr ORDER BY i;
+SELECT * FROM inet_tbl WHERE i >> '192.168.1.0/24'::cidr ORDER BY i;
+SELECT * FROM inet_tbl WHERE i < '192.168.1.0/24'::cidr ORDER BY i;
+SELECT * FROM inet_tbl WHERE i <= '192.168.1.0/24'::cidr ORDER BY i;
+SELECT * FROM inet_tbl WHERE i = '192.168.1.0/24'::cidr ORDER BY i;
+SELECT * FROM inet_tbl WHERE i >= '192.168.1.0/24'::cidr ORDER BY i;
+SELECT * FROM inet_tbl WHERE i > '192.168.1.0/24'::cidr ORDER BY i;
+SELECT * FROM inet_tbl WHERE i <> '192.168.1.0/24'::cidr ORDER BY i;
+SET enable_seqscan TO on;
+DROP INDEX inet_idx2;
+
 -- simple tests of inet boolean and arithmetic operators
 SELECT i, ~i AS "~i" FROM inet_tbl;
 SELECT i, c, i & c AS "and" FROM inet_tbl;
-- 
GitLab