Add an in-core GiST index opclass for inet/cidr types.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 8 Apr 2014 19:46:14 +0000 (15:46 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 8 Apr 2014 19:46:43 +0000 (15:46 -0400)
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

21 files changed:
doc/src/sgml/func.sgml
doc/src/sgml/gin.sgml
doc/src/sgml/gist.sgml
doc/src/sgml/indices.sgml
doc/src/sgml/spgist.sgml
src/backend/utils/adt/Makefile
src/backend/utils/adt/network.c
src/backend/utils/adt/network_gist.c [new file with mode: 0644]
src/backend/utils/adt/network_selfuncs.c [new file with mode: 0644]
src/include/catalog/catversion.h
src/include/catalog/pg_amop.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_opclass.h
src/include/catalog/pg_operator.h
src/include/catalog/pg_opfamily.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/include/utils/inet.h
src/test/regress/expected/inet.out
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/inet.sql

index 73764f2bedc29d44f9749e0fb1163d5bec48f745..0809a6d2e921a8fccb9418175062a6e5f93c61c7 100644 (file)
@@ -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>
index 561608f8fa8079657000bfa1c675a684e57b2544..576ad3005aa18754bd8aa5a9e60ff5a1352fb5fe 100644 (file)
  </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>
 
index ed0bc54f52d672c4e69be6e72684bcba2428b45d..0158b1759e84b07354c212c818601c425a1103b5 100644 (file)
 
 </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>
 
index b1c8f22718576182a51c28e381c8a1665ca6d994..64530a11c86f7cf12f94ed162e9124d0065b04ca 100644 (file)
@@ -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:
index a043ffb06c464f103f166373ac4efe80cdc92814..56827e520dd62a7a07ac2959e8530143a3fb7dc9 100644 (file)
 
 </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>
 
index 6b23069e26c36ce1303adcc828df9caeb88eb09a..7b4391bba179082c21e9ff526b1f8276687dfce4 100644 (file)
@@ -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 \
index 5e837fab2def2ca4272d04f32a805c782fc76635..8bdf5778d89e4fa9f7e1a47084c8c8518a95b881 100644 (file)
 
 
 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 (file)
index 0000000..0a826ae
--- /dev/null
@@ -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 (file)
index 0000000..d0d806f
--- /dev/null
@@ -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);
+}
index 4b3357ccd9710ba7f42a9e9afddcaac895fd6ca6..fe6144e2d344daaaa630e94a24b83ee1214d4e14 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201404081
+#define CATALOG_VERSION_NO 201404082
 
 #endif
index 262311355702a35b26fda0696acf5bec093ea085..8efd3be3c668a196c60b6a1a18dc69e568267eb4 100644 (file)
@@ -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 */
index b28dd563a8c9f0de05a309621f41adb583551c42..198b126964f8f0d33912e3bd2bfa34d31d4da6e1 100644 (file)
@@ -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 ));
index 63a40a8412cce81c4d609eaec62169010ae7b201..49b24108de972073e2de3088aa2e4631c28a4c48 100644 (file)
@@ -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 ));
index ac09034f3d0e5eb1d522f2e84654d479dbfb9003..f280af441c27723dd177b446d0469eb51e4955e8 100644 (file)
@@ -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");
index 775be86c1af34db287f81d9cf0a94df41fa56c8a..9e8f4ac5b6310edf3ad3e2652bfa0b3556842481 100644 (file)
@@ -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 ));
index 21c17a08ed4c324634fff79e3a53782f3e08c114..7b9c5870fac6c86b2cc42bbe46d938412fed709a 100644 (file)
@@ -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_ ));
index 720c83180113e8642b0b2b66b408b6dd8827979b..5907cb13fd31d0b5cd0ca5a61311c32c5864f7eb 100644 (file)
@@ -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);
index 330f32de8f1c03c12060edbd8c905b20aa26f3e5..bd31c7169a19d28212f547a0442e02f512bdb005 100644 (file)
@@ -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 */
index 356a397822dbbac2cde1c8bd017c075d8dc0cef5..008cc0b5ddf05ec8361f88c53c564587cd0566ea 100644 (file)
@@ -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                     
index bf765014357a352ba1ec53812d35edc75ba559c5..118f7e43dc09ca18db44c7d64e11ad9d1d818443 100644 (file)
@@ -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
index 328f14907ba21cae2181d46566af962658c5f826..be078fbb8471f958ae800966bdeb8a960d85e377 100644 (file)
@@ -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;