mirror of
https://github.com/postgres/postgres.git
synced 2025-05-03 22:24:49 +03:00
Create an SP-GiST opclass for inet/cidr.
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>
This commit is contained in:
parent
0fda682e54
commit
77e2906821
@ -145,6 +145,23 @@
|
||||
<literal>~>~</>
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>inet_ops</></entry>
|
||||
<entry><type>inet</>, <type>cidr</></entry>
|
||||
<entry>
|
||||
<literal>&&</>
|
||||
<literal>>></>
|
||||
<literal>>>=</>
|
||||
<literal>></>
|
||||
<literal>>=</>
|
||||
<literal><></>
|
||||
<literal><<</>
|
||||
<literal><<=</>
|
||||
<literal><</>
|
||||
<literal><=</>
|
||||
<literal>=</>
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
@ -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 \
|
||||
|
708
src/backend/utils/adt/network_spgist.c
Normal file
708
src/backend/utils/adt/network_spgist.c
Normal file
@ -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;
|
||||
}
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201608191
|
||||
#define CATALOG_VERSION_NO 201608231
|
||||
|
||||
#endif
|
||||
|
@ -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 ));
|
||||
|
@ -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 ));
|
||||
|
@ -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 ));
|
||||
|
@ -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 ));
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user