]> granicus.if.org Git - postgresql/commitdiff
Create an SP-GiST opclass for inet/cidr.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 23 Aug 2016 19:16:21 +0000 (15:16 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 23 Aug 2016 19:16:30 +0000 (15:16 -0400)
This seems to offer significantly better search performance than the
existing GiST opclass for inet/cidr, at least on data with a wide mix
of network mask lengths.  (That may suggest that the data splitting
heuristics in the GiST opclass could be improved.)

Emre Hasegeli, with mostly-cosmetic adjustments by me

Discussion: <CAE2gYzxtth9qatW_OAqdOjykS0bxq7AYHLuyAQLPgT7H9ZU0Cw@mail.gmail.com>

13 files changed:
doc/src/sgml/spgist.sgml
src/backend/utils/adt/Makefile
src/backend/utils/adt/network_spgist.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_opfamily.h
src/include/catalog/pg_proc.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 dfa62adb5571f718de397313a15605d7d9e13267..d60aa23f33bc00b18d17d62bbbc99b04692b4fed 100644 (file)
        <literal>~&gt;~</>
       </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>
+     </row>
     </tbody>
    </tgroup>
   </table>
index b9e217ae49c4aa6c9692807083cc4304aaf175ca..0f512753e496fd57f26b2855041585c13b48b799 100644 (file)
@@ -17,7 +17,7 @@ OBJS = acl.o amutils.o arrayfuncs.o array_expanded.o array_selfuncs.o \
        geo_ops.o geo_selfuncs.o geo_spgist.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 network_gist.o network_selfuncs.o \
+       network.o network_gist.o network_selfuncs.o network_spgist.o \
        numeric.o numutils.o oid.o oracle_compat.o \
        orderedsetaggs.o pg_locale.o pg_lsn.o pg_upgrade_support.o \
        pgstatfuncs.o \
diff --git a/src/backend/utils/adt/network_spgist.c b/src/backend/utils/adt/network_spgist.c
new file mode 100644 (file)
index 0000000..708ae89
--- /dev/null
@@ -0,0 +1,708 @@
+/*-------------------------------------------------------------------------
+ *
+ * network_spgist.c
+ *       SP-GiST support for network types.
+ *
+ * We split inet index entries first by address family (IPv4 or IPv6).
+ * If the entries below a given inner tuple are all of the same family,
+ * we identify their common prefix and split by the next bit of the address,
+ * and by whether their masklens exceed the length of the common prefix.
+ *
+ * An inner tuple that has both IPv4 and IPv6 children has a null prefix
+ * and exactly two nodes, the first being for IPv4 and the second for IPv6.
+ *
+ * Otherwise, the prefix is a CIDR value representing the common prefix,
+ * and there are exactly four nodes.  Node numbers 0 and 1 are for addresses
+ * with the same masklen as the prefix, while node numbers 2 and 3 are for
+ * addresses with larger masklen.  (We do not allow a tuple to contain
+ * entries with masklen smaller than its prefix's.)  Node numbers 0 and 1
+ * are distinguished by the next bit of the address after the common prefix,
+ * and likewise for node numbers 2 and 3.  If there are no more bits in
+ * the address family, everything goes into node 0 (which will probably
+ * lead to creating an allTheSame tuple).
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *                     src/backend/utils/adt/network_spgist.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/spgist.h"
+#include "catalog/pg_type.h"
+#include "utils/inet.h"
+
+
+static int     inet_spg_node_number(const inet *val, int commonbits);
+static int inet_spg_consistent_bitmap(const inet *prefix, int nkeys,
+                                                  ScanKey scankeys, bool leaf);
+
+/*
+ * The SP-GiST configuration function
+ */
+Datum
+inet_spg_config(PG_FUNCTION_ARGS)
+{
+       /* spgConfigIn *cfgin = (spgConfigIn *) PG_GETARG_POINTER(0); */
+       spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1);
+
+       cfg->prefixType = CIDROID;
+       cfg->labelType = VOIDOID;
+       cfg->canReturnData = true;
+       cfg->longValuesOK = false;
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * The SP-GiST choose function
+ */
+Datum
+inet_spg_choose(PG_FUNCTION_ARGS)
+{
+       spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0);
+       spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1);
+       inet       *val = DatumGetInetPP(in->datum),
+                          *prefix;
+       int                     commonbits;
+
+       /*
+        * If we're looking at a tuple that splits by address family, choose the
+        * appropriate subnode.
+        */
+       if (!in->hasPrefix)
+       {
+               /* allTheSame isn't possible for such a tuple */
+               Assert(!in->allTheSame);
+               Assert(in->nNodes == 2);
+
+               out->resultType = spgMatchNode;
+               out->result.matchNode.nodeN = (ip_family(val) == PGSQL_AF_INET) ? 0 : 1;
+               out->result.matchNode.restDatum = InetPGetDatum(val);
+
+               PG_RETURN_VOID();
+       }
+
+       /* Else it must split by prefix */
+       Assert(in->nNodes == 4 || in->allTheSame);
+
+       prefix = DatumGetInetPP(in->prefixDatum);
+       commonbits = ip_bits(prefix);
+
+       /*
+        * We cannot put addresses from different families under the same inner
+        * node, so we have to split if the new value's family is different.
+        */
+       if (ip_family(val) != ip_family(prefix))
+       {
+               /* Set up 2-node tuple */
+               out->resultType = spgSplitTuple;
+               out->result.splitTuple.prefixHasPrefix = false;
+               out->result.splitTuple.prefixNNodes = 2;
+               out->result.splitTuple.prefixNodeLabels = NULL;
+
+               /* Identify which node the existing data goes into */
+               out->result.splitTuple.childNodeN =
+                       (ip_family(prefix) == PGSQL_AF_INET) ? 0 : 1;
+
+               out->result.splitTuple.postfixHasPrefix = true;
+               out->result.splitTuple.postfixPrefixDatum = InetPGetDatum(prefix);
+
+               PG_RETURN_VOID();
+       }
+
+       /*
+        * If the new value does not match the existing prefix, we have to split.
+        */
+       if (ip_bits(val) < commonbits ||
+               bitncmp(ip_addr(prefix), ip_addr(val), commonbits) != 0)
+       {
+               /* Determine new prefix length for the split tuple */
+               commonbits = bitncommon(ip_addr(prefix), ip_addr(val),
+                                                               Min(ip_bits(val), commonbits));
+
+               /* Set up 4-node tuple */
+               out->resultType = spgSplitTuple;
+               out->result.splitTuple.prefixHasPrefix = true;
+               out->result.splitTuple.prefixPrefixDatum =
+                       InetPGetDatum(cidr_set_masklen_internal(val, commonbits));
+               out->result.splitTuple.prefixNNodes = 4;
+               out->result.splitTuple.prefixNodeLabels = NULL;
+
+               /* Identify which node the existing data goes into */
+               out->result.splitTuple.childNodeN =
+                       inet_spg_node_number(prefix, commonbits);
+
+               out->result.splitTuple.postfixHasPrefix = true;
+               out->result.splitTuple.postfixPrefixDatum = InetPGetDatum(prefix);
+
+               PG_RETURN_VOID();
+       }
+
+       /*
+        * All OK, choose the node to descend into.  (If this tuple is marked
+        * allTheSame, the core code will ignore our choice of nodeN; but we need
+        * not account for that case explicitly here.)
+        */
+       out->resultType = spgMatchNode;
+       out->result.matchNode.nodeN = inet_spg_node_number(val, commonbits);
+       out->result.matchNode.restDatum = InetPGetDatum(val);
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * The GiST PickSplit method
+ */
+Datum
+inet_spg_picksplit(PG_FUNCTION_ARGS)
+{
+       spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0);
+       spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1);
+       inet       *prefix,
+                          *tmp;
+       int                     i,
+                               commonbits;
+       bool            differentFamilies = false;
+
+       /* Initialize the prefix with the first item */
+       prefix = DatumGetInetPP(in->datums[0]);
+       commonbits = ip_bits(prefix);
+
+       /* Examine remaining items to discover minimum common prefix length */
+       for (i = 1; i < in->nTuples; i++)
+       {
+               tmp = DatumGetInetPP(in->datums[i]);
+
+               if (ip_family(tmp) != ip_family(prefix))
+               {
+                       differentFamilies = true;
+                       break;
+               }
+
+               if (ip_bits(tmp) < commonbits)
+                       commonbits = ip_bits(tmp);
+               commonbits = bitncommon(ip_addr(prefix), ip_addr(tmp), commonbits);
+               if (commonbits == 0)
+                       break;
+       }
+
+       /* Don't need labels; allocate output arrays */
+       out->nodeLabels = NULL;
+       out->mapTuplesToNodes = (int *) palloc(sizeof(int) * in->nTuples);
+       out->leafTupleDatums = (Datum *) palloc(sizeof(Datum) * in->nTuples);
+
+       if (differentFamilies)
+       {
+               /* Set up 2-node tuple */
+               out->hasPrefix = false;
+               out->nNodes = 2;
+
+               for (i = 0; i < in->nTuples; i++)
+               {
+                       tmp = DatumGetInetPP(in->datums[i]);
+                       out->mapTuplesToNodes[i] =
+                               (ip_family(tmp) == PGSQL_AF_INET) ? 0 : 1;
+                       out->leafTupleDatums[i] = InetPGetDatum(tmp);
+               }
+       }
+       else
+       {
+               /* Set up 4-node tuple */
+               out->hasPrefix = true;
+               out->prefixDatum =
+                       InetPGetDatum(cidr_set_masklen_internal(prefix, commonbits));
+               out->nNodes = 4;
+
+               for (i = 0; i < in->nTuples; i++)
+               {
+                       tmp = DatumGetInetPP(in->datums[i]);
+                       out->mapTuplesToNodes[i] = inet_spg_node_number(tmp, commonbits);
+                       out->leafTupleDatums[i] = InetPGetDatum(tmp);
+               }
+       }
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * The SP-GiST query consistency check for inner tuples
+ */
+Datum
+inet_spg_inner_consistent(PG_FUNCTION_ARGS)
+{
+       spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
+       spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
+       int                     i;
+       int                     which;
+
+       if (!in->hasPrefix)
+       {
+               Assert(!in->allTheSame);
+               Assert(in->nNodes == 2);
+
+               /* Identify which child nodes need to be visited */
+               which = 1 | (1 << 1);
+
+               for (i = 0; i < in->nkeys; i++)
+               {
+                       StrategyNumber strategy = in->scankeys[i].sk_strategy;
+                       inet       *argument = DatumGetInetPP(in->scankeys[i].sk_argument);
+
+                       switch (strategy)
+                       {
+                               case RTLessStrategyNumber:
+                               case RTLessEqualStrategyNumber:
+                                       if (ip_family(argument) == PGSQL_AF_INET)
+                                               which &= 1;
+                                       break;
+
+                               case RTGreaterEqualStrategyNumber:
+                               case RTGreaterStrategyNumber:
+                                       if (ip_family(argument) == PGSQL_AF_INET6)
+                                               which &= (1 << 1);
+                                       break;
+
+                               case RTNotEqualStrategyNumber:
+                                       break;
+
+                               default:
+                                       /* all other ops can only match addrs of same family */
+                                       if (ip_family(argument) == PGSQL_AF_INET)
+                                               which &= 1;
+                                       else
+                                               which &= (1 << 1);
+                                       break;
+                       }
+               }
+       }
+       else if (!in->allTheSame)
+       {
+               Assert(in->nNodes == 4);
+
+               /* Identify which child nodes need to be visited */
+               which = inet_spg_consistent_bitmap(DatumGetInetPP(in->prefixDatum),
+                                                                                  in->nkeys, in->scankeys, false);
+       }
+       else
+       {
+               /* Must visit all nodes; we assume there are less than 32 of 'em */
+               which = ~0;
+       }
+
+       out->nNodes = 0;
+
+       if (which)
+       {
+               out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
+
+               for (i = 0; i < in->nNodes; i++)
+               {
+                       if (which & (1 << i))
+                       {
+                               out->nodeNumbers[out->nNodes] = i;
+                               out->nNodes++;
+                       }
+               }
+       }
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * The SP-GiST query consistency check for leaf tuples
+ */
+Datum
+inet_spg_leaf_consistent(PG_FUNCTION_ARGS)
+{
+       spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0);
+       spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1);
+       inet       *leaf = DatumGetInetPP(in->leafDatum);
+
+       /* All tests are exact. */
+       out->recheck = false;
+
+       /* Leaf is what it is... */
+       out->leafValue = InetPGetDatum(leaf);
+
+       /* Use common code to apply the tests. */
+       PG_RETURN_BOOL(inet_spg_consistent_bitmap(leaf, in->nkeys, in->scankeys,
+                                                                                         true));
+}
+
+/*
+ * Calculate node number (within a 4-node, single-family inner index tuple)
+ *
+ * The value must have the same family as the node's prefix, and
+ * commonbits is the mask length of the prefix.  We use even or odd
+ * nodes according to the next address bit after the commonbits,
+ * and low or high nodes according to whether the value's mask length
+ * is larger than commonbits.
+ */
+static int
+inet_spg_node_number(const inet *val, int commonbits)
+{
+       int                     nodeN = 0;
+
+       if (commonbits < ip_maxbits(val) &&
+               ip_addr(val)[commonbits / 8] & (1 << (7 - commonbits % 8)))
+               nodeN |= 1;
+       if (commonbits < ip_bits(val))
+               nodeN |= 2;
+
+       return nodeN;
+}
+
+/*
+ * Calculate bitmap of node numbers that are consistent with the query
+ *
+ * This can be used either at a 4-way inner tuple, or at a leaf tuple.
+ * In the latter case, we should return a boolean result (0 or 1)
+ * not a bitmap.
+ *
+ * This definition is pretty odd, but the inner and leaf consistency checks
+ * are mostly common and it seems best to keep them in one function.
+ */
+static int
+inet_spg_consistent_bitmap(const inet *prefix, int nkeys, ScanKey scankeys,
+                                                  bool leaf)
+{
+       int                     bitmap;
+       int                     commonbits,
+                               i;
+
+       /* Initialize result to allow visiting all children */
+       if (leaf)
+               bitmap = 1;
+       else
+               bitmap = 1 | (1 << 1) | (1 << 2) | (1 << 3);
+
+       commonbits = ip_bits(prefix);
+
+       for (i = 0; i < nkeys; i++)
+       {
+               inet       *argument = DatumGetInetPP(scankeys[i].sk_argument);
+               StrategyNumber strategy = scankeys[i].sk_strategy;
+               int                     order;
+
+               /*
+                * Check 0: different families
+                *
+                * Matching families do not help any of the strategies.
+                */
+               if (ip_family(argument) != ip_family(prefix))
+               {
+                       switch (strategy)
+                       {
+                               case RTLessStrategyNumber:
+                               case RTLessEqualStrategyNumber:
+                                       if (ip_family(argument) < ip_family(prefix))
+                                               bitmap = 0;
+                                       break;
+
+                               case RTGreaterEqualStrategyNumber:
+                               case RTGreaterStrategyNumber:
+                                       if (ip_family(argument) > ip_family(prefix))
+                                               bitmap = 0;
+                                       break;
+
+                               case RTNotEqualStrategyNumber:
+                                       break;
+
+                               default:
+                                       /* For all other cases, we can be sure there is no match */
+                                       bitmap = 0;
+                                       break;
+                       }
+
+                       if (!bitmap)
+                               break;
+
+                       /* Other checks make no sense with different families. */
+                       continue;
+               }
+
+               /*
+                * Check 1: 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 greater ip_bits, so we can avoid descending in some cases
+                * too.
+                *
+                * This check is less expensive than checking the address bits, so we
+                * are doing this before, but it has to be done after for the basic
+                * comparison strategies, because ip_bits only affect their results
+                * when the common network bits are the same.
+                */
+               switch (strategy)
+               {
+                       case RTSubStrategyNumber:
+                               if (commonbits <= ip_bits(argument))
+                                       bitmap &= (1 << 2) | (1 << 3);
+                               break;
+
+                       case RTSubEqualStrategyNumber:
+                               if (commonbits < ip_bits(argument))
+                                       bitmap &= (1 << 2) | (1 << 3);
+                               break;
+
+                       case RTSuperStrategyNumber:
+                               if (commonbits == ip_bits(argument) - 1)
+                                       bitmap &= 1 | (1 << 1);
+                               else if (commonbits >= ip_bits(argument))
+                                       bitmap = 0;
+                               break;
+
+                       case RTSuperEqualStrategyNumber:
+                               if (commonbits == ip_bits(argument))
+                                       bitmap &= 1 | (1 << 1);
+                               else if (commonbits > ip_bits(argument))
+                                       bitmap = 0;
+                               break;
+
+                       case RTEqualStrategyNumber:
+                               if (commonbits < ip_bits(argument))
+                                       bitmap &= (1 << 2) | (1 << 3);
+                               else if (commonbits == ip_bits(argument))
+                                       bitmap &= 1 | (1 << 1);
+                               else
+                                       bitmap = 0;
+                               break;
+               }
+
+               if (!bitmap)
+                       break;
+
+               /*
+                * Check 2: 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 can
+                * eliminate some cases.
+                */
+               order = bitncmp(ip_addr(prefix), ip_addr(argument),
+                                               Min(commonbits, ip_bits(argument)));
+
+               if (order != 0)
+               {
+                       switch (strategy)
+                       {
+                               case RTLessStrategyNumber:
+                               case RTLessEqualStrategyNumber:
+                                       if (order > 0)
+                                               bitmap = 0;
+                                       break;
+
+                               case RTGreaterEqualStrategyNumber:
+                               case RTGreaterStrategyNumber:
+                                       if (order < 0)
+                                               bitmap = 0;
+                                       break;
+
+                               case RTNotEqualStrategyNumber:
+                                       break;
+
+                               default:
+                                       /* For all other cases, we can be sure there is no match */
+                                       bitmap = 0;
+                                       break;
+                       }
+
+                       if (!bitmap)
+                               break;
+
+                       /*
+                        * Remaining checks make no sense when common bits don't match.
+                        */
+                       continue;
+               }
+
+               /*
+                * Check 3: next network bit
+                *
+                * We can filter out branch 2 or 3 using the next network bit of the
+                * argument, if it is available.
+                *
+                * This check matters for the performance of the search. The results
+                * would be correct without it.
+                */
+               if (bitmap & ((1 << 2) | (1 << 3)) &&
+                       commonbits < ip_bits(argument))
+               {
+                       int                     nextbit;
+
+                       nextbit = ip_addr(argument)[commonbits / 8] &
+                               (1 << (7 - commonbits % 8));
+
+                       switch (strategy)
+                       {
+                               case RTLessStrategyNumber:
+                               case RTLessEqualStrategyNumber:
+                                       if (!nextbit)
+                                               bitmap &= 1 | (1 << 1) | (1 << 2);
+                                       break;
+
+                               case RTGreaterEqualStrategyNumber:
+                               case RTGreaterStrategyNumber:
+                                       if (nextbit)
+                                               bitmap &= 1 | (1 << 1) | (1 << 3);
+                                       break;
+
+                               case RTNotEqualStrategyNumber:
+                                       break;
+
+                               default:
+                                       if (!nextbit)
+                                               bitmap &= 1 | (1 << 1) | (1 << 2);
+                                       else
+                                               bitmap &= 1 | (1 << 1) | (1 << 3);
+                                       break;
+                       }
+
+                       if (!bitmap)
+                               break;
+               }
+
+               /*
+                * Remaining checks are only for the basic comparison strategies. This
+                * test relies on the strategy number ordering defined in stratnum.h.
+                */
+               if (strategy < RTEqualStrategyNumber ||
+                       strategy > RTGreaterEqualStrategyNumber)
+                       continue;
+
+               /*
+                * Check 4: network bit count
+                *
+                * At this point, we know that the common network bits of the prefix
+                * and the argument are the same, so we can go forward and check the
+                * ip_bits.
+                */
+               switch (strategy)
+               {
+                       case RTLessStrategyNumber:
+                       case RTLessEqualStrategyNumber:
+                               if (commonbits == ip_bits(argument))
+                                       bitmap &= 1 | (1 << 1);
+                               else if (commonbits > ip_bits(argument))
+                                       bitmap = 0;
+                               break;
+
+                       case RTGreaterEqualStrategyNumber:
+                       case RTGreaterStrategyNumber:
+                               if (commonbits < ip_bits(argument))
+                                       bitmap &= (1 << 2) | (1 << 3);
+                               break;
+               }
+
+               if (!bitmap)
+                       break;
+
+               /* Remaining checks don't make sense with different ip_bits. */
+               if (commonbits != ip_bits(argument))
+                       continue;
+
+               /*
+                * Check 5: next host bit
+                *
+                * We can filter out branch 0 or 1 using the next host bit of the
+                * argument, if it is available.
+                *
+                * This check matters for the performance of the search. The results
+                * would be correct without it.  There is no point in running it for
+                * leafs as we have to check the whole address on the next step.
+                */
+               if (!leaf && bitmap & (1 | (1 << 1)) &&
+                       commonbits < ip_maxbits(argument))
+               {
+                       int                     nextbit;
+
+                       nextbit = ip_addr(argument)[commonbits / 8] &
+                               (1 << (7 - commonbits % 8));
+
+                       switch (strategy)
+                       {
+                               case RTLessStrategyNumber:
+                               case RTLessEqualStrategyNumber:
+                                       if (!nextbit)
+                                               bitmap &= 1 | (1 << 2) | (1 << 3);
+                                       break;
+
+                               case RTGreaterEqualStrategyNumber:
+                               case RTGreaterStrategyNumber:
+                                       if (nextbit)
+                                               bitmap &= (1 << 1) | (1 << 2) | (1 << 3);
+                                       break;
+
+                               case RTNotEqualStrategyNumber:
+                                       break;
+
+                               default:
+                                       if (!nextbit)
+                                               bitmap &= 1 | (1 << 2) | (1 << 3);
+                                       else
+                                               bitmap &= (1 << 1) | (1 << 2) | (1 << 3);
+                                       break;
+                       }
+
+                       if (!bitmap)
+                               break;
+               }
+
+               /*
+                * Check 6: whole address
+                *
+                * This is the last check for correctness of the basic comparison
+                * strategies.  It's only appropriate at leaf entries.
+                */
+               if (leaf)
+               {
+                       /* Redo ordering comparison using all address bits */
+                       order = bitncmp(ip_addr(prefix), ip_addr(argument),
+                                                       ip_maxbits(prefix));
+
+                       switch (strategy)
+                       {
+                               case RTLessStrategyNumber:
+                                       if (order >= 0)
+                                               bitmap = 0;
+                                       break;
+
+                               case RTLessEqualStrategyNumber:
+                                       if (order > 0)
+                                               bitmap = 0;
+                                       break;
+
+                               case RTEqualStrategyNumber:
+                                       if (order != 0)
+                                               bitmap = 0;
+                                       break;
+
+                               case RTGreaterEqualStrategyNumber:
+                                       if (order < 0)
+                                               bitmap = 0;
+                                       break;
+
+                               case RTGreaterStrategyNumber:
+                                       if (order <= 0)
+                                               bitmap = 0;
+                                       break;
+
+                               case RTNotEqualStrategyNumber:
+                                       if (order == 0)
+                                               bitmap = 0;
+                                       break;
+                       }
+
+                       if (!bitmap)
+                               break;
+               }
+       }
+
+       return bitmap;
+}
index 26f6126002cad3830b8c11b3df6b3dc02a5c46c9..c04edadbf0964edba51b6f9e42905308ca156258 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201608191
+#define CATALOG_VERSION_NO     201608231
 
 #endif
index a15b0ec309b3507afa39c7fa7b5ca8e8afbcb017..917ed46b71ae11e6b00b08a2c25bcdb9e32ee231 100644 (file)
@@ -863,6 +863,21 @@ 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 ));
 
+/*
+ * SP-GiST inet_ops
+ */
+DATA(insert (  3794    869 869 3 s             3552 4000 0 ));
+DATA(insert (  3794    869 869 18 s    1201 4000 0 ));
+DATA(insert (  3794    869 869 19 s    1202 4000 0 ));
+DATA(insert (  3794    869 869 20 s    1203 4000 0 ));
+DATA(insert (  3794    869 869 21 s    1204 4000 0 ));
+DATA(insert (  3794    869 869 22 s    1205 4000 0 ));
+DATA(insert (  3794    869 869 23 s    1206 4000 0 ));
+DATA(insert (  3794    869 869 24 s    931 4000 0 ));
+DATA(insert (  3794    869 869 25 s    932 4000 0 ));
+DATA(insert (  3794    869 869 26 s    933 4000 0 ));
+DATA(insert (  3794    869 869 27 s    934 4000 0 ));
+
 /* BRIN opclasses */
 /* minmax bytea */
 DATA(insert (  4064     17   17 1 s      1957    3580 0 ));
index 00320b4c3343d0c8169f68ccb670f1f049448315..0cbb416392047934f4624ac387f38d3985d5d5d1 100644 (file)
@@ -428,6 +428,11 @@ DATA(insert (      3474   3831 3831 2 3470 ));
 DATA(insert (  3474   3831 3831 3 3471 ));
 DATA(insert (  3474   3831 3831 4 3472 ));
 DATA(insert (  3474   3831 3831 5 3473 ));
+DATA(insert (  3794   869 869 1 3795 ));
+DATA(insert (  3794   869 869 2 3796 ));
+DATA(insert (  3794   869 869 3 3797 ));
+DATA(insert (  3794   869 869 4 3798 ));
+DATA(insert (  3794   869 869 5 3799 ));
 DATA(insert (  4015   600 600 1 4018 ));
 DATA(insert (  4015   600 600 2 4019 ));
 DATA(insert (  4015   600 600 3 4020 ));
index 6c82d946004a08c36e3a5474b73c750d57005f6e..f40b06112b158456e4e8022df56e1cc841348784 100644 (file)
@@ -113,6 +113,7 @@ 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 (  4000    inet_ops                        PGNSP PGUID 3794  869 t 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 1499a502f32380c956b4cc07d6dbc9f964a95179..ac6b3047873e3848724278827c3b1d27f74f8921 100644 (file)
@@ -79,6 +79,7 @@ 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 = 3794 (       4000    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 050a98c39720d2faeb654d1ecf9ca65014c8aedd..e2d08babda8647c36e5538dfdb7005aa737097ee 100644 (file)
@@ -2212,6 +2212,18 @@ DESCR("GiST support");
 DATA(insert OID = 3559 (  inet_gist_same               PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "869 869 2281" _null_ _null_ _null_ _null_ _null_ inet_gist_same _null_ _null_ _null_ ));
 DESCR("GiST support");
 
+/* SP-GiST support for inet and cidr */
+DATA(insert OID = 3795 (  inet_spg_config              PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ _null_ inet_spg_config _null_ _null_ _null_ ));
+DESCR("SP-GiST support");
+DATA(insert OID = 3796 (  inet_spg_choose              PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ _null_ inet_spg_choose _null_ _null_ _null_ ));
+DESCR("SP-GiST support");
+DATA(insert OID = 3797 (  inet_spg_picksplit   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ _null_ inet_spg_picksplit _null_ _null_ _null_ ));
+DESCR("SP-GiST support");
+DATA(insert OID = 3798 (  inet_spg_inner_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ _null_ inet_spg_inner_consistent _null_ _null_ _null_ ));
+DESCR("SP-GiST support");
+DATA(insert OID = 3799 (  inet_spg_leaf_consistent     PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ inet_spg_leaf_consistent _null_ _null_ _null_ ));
+DESCR("SP-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 s 4 0 701 "2281 26 2281 23" _null_ _null_ _null_ _null_ _null_  networksel _null_ _null_ _null_ ));
 DESCR("restriction selectivity for network operators");
index dfa0b9f7113beda8ababb413dadeaf2175df2b1e..9fd954da7d3737cf505622df90dbeda1abca5a83 100644 (file)
@@ -135,6 +135,15 @@ extern Datum inet_gist_penalty(PG_FUNCTION_ARGS);
 extern Datum inet_gist_picksplit(PG_FUNCTION_ARGS);
 extern Datum inet_gist_same(PG_FUNCTION_ARGS);
 
+/*
+ * SP-GiST support functions in network_spgist.c
+ */
+extern Datum inet_spg_config(PG_FUNCTION_ARGS);
+extern Datum inet_spg_choose(PG_FUNCTION_ARGS);
+extern Datum inet_spg_picksplit(PG_FUNCTION_ARGS);
+extern Datum inet_spg_inner_consistent(PG_FUNCTION_ARGS);
+extern Datum inet_spg_leaf_consistent(PG_FUNCTION_ARGS);
+
 /*
  * Estimation functions in network_selfuncs.c
  */
index 9447e03ab562205791818d34d65fa8104b84c132..be9427eb6b8178f7a095d1896207f9f9cb72145c 100644 (file)
@@ -411,6 +411,154 @@ SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i;
 
 SET enable_seqscan TO on;
 DROP INDEX inet_idx2;
+-- check that spgist index works correctly
+CREATE INDEX inet_idx3 ON inet_tbl using spgist (i);
+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)
+
+-- test index-only scans
+EXPLAIN (COSTS OFF)
+SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i;
+                    QUERY PLAN                     
+---------------------------------------------------
+ Sort
+   Sort Key: i
+   ->  Index Only Scan using inet_idx3 on inet_tbl
+         Index Cond: (i << '192.168.1.0/24'::inet)
+(4 rows)
+
+SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i;
+        i         
+------------------
+ 192.168.1.0/25
+ 192.168.1.255/25
+ 192.168.1.226
+(3 rows)
+
+SET enable_seqscan TO on;
+DROP INDEX inet_idx3;
 -- simple tests of inet boolean and arithmetic operators
 SELECT i, ~i AS "~i" FROM inet_tbl;
         i         |                     ~i                     
index 826441442b72c3e1c0ea4f082de99924cf99d94a..0bcec136c53784cf3729f44fafc351606c67bccc 100644 (file)
@@ -1819,7 +1819,16 @@ ORDER BY 1, 2, 3;
        4000 |           15 | >
        4000 |           16 | @>
        4000 |           18 | =
-(112 rows)
+       4000 |           19 | <>
+       4000 |           20 | <
+       4000 |           21 | <=
+       4000 |           22 | >
+       4000 |           23 | >=
+       4000 |           24 | <<
+       4000 |           25 | <<=
+       4000 |           26 | >>
+       4000 |           27 | >>=
+(121 rows)
 
 -- Check that all opclass search operators have selectivity estimators.
 -- This is not absolutely required, but it seems a reasonable thing
index 007741e9359b44c4aa7e1e0f4d328912f899ba6f..880e115360d98356add13e2949d428ed4ddee035 100644 (file)
@@ -93,6 +93,29 @@ SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i;
 SET enable_seqscan TO on;
 DROP INDEX inet_idx2;
 
+-- check that spgist index works correctly
+CREATE INDEX inet_idx3 ON inet_tbl using spgist (i);
+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;
+
+-- test index-only scans
+EXPLAIN (COSTS OFF)
+SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i;
+SELECT i FROM inet_tbl WHERE i << '192.168.1.0/24'::cidr ORDER BY i;
+
+SET enable_seqscan TO on;
+DROP INDEX inet_idx3;
+
 -- 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;