From: Tom Lane Date: Tue, 23 Aug 2016 19:16:21 +0000 (-0400) Subject: Create an SP-GiST opclass for inet/cidr. X-Git-Tag: REL_10_BETA1~1831 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=77e2906821e2aec3c0807866a84c2934feeac8be;p=postgresql 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: --- diff --git a/doc/src/sgml/spgist.sgml b/doc/src/sgml/spgist.sgml index dfa62adb55..d60aa23f33 100644 --- a/doc/src/sgml/spgist.sgml +++ b/doc/src/sgml/spgist.sgml @@ -145,6 +145,23 @@ ~>~ + + inet_ops + inet, cidr + + && + >> + >>= + > + >= + <> + << + <<= + < + <= + = + + diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index b9e217ae49..0f512753e4 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -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 index 0000000000..708ae899ac --- /dev/null +++ b/src/backend/utils/adt/network_spgist.c @@ -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; +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 26f6126002..c04edadbf0 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201608191 +#define CATALOG_VERSION_NO 201608231 #endif diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index a15b0ec309..917ed46b71 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -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 )); diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 00320b4c33..0cbb416392 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -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 )); diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index 6c82d94600..f40b06112b 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -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 )); diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h index 1499a502f3..ac6b304787 100644 --- a/src/include/catalog/pg_opfamily.h +++ b/src/include/catalog/pg_opfamily.h @@ -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 )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 050a98c397..e2d08babda 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -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"); diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h index dfa0b9f711..9fd954da7d 100644 --- a/src/include/utils/inet.h +++ b/src/include/utils/inet.h @@ -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 */ diff --git a/src/test/regress/expected/inet.out b/src/test/regress/expected/inet.out index 9447e03ab5..be9427eb6b 100644 --- a/src/test/regress/expected/inet.out +++ b/src/test/regress/expected/inet.out @@ -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 diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 826441442b..0bcec136c5 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -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 diff --git a/src/test/regress/sql/inet.sql b/src/test/regress/sql/inet.sql index 007741e935..880e115360 100644 --- a/src/test/regress/sql/inet.sql +++ b/src/test/regress/sql/inet.sql @@ -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;