summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/adt/network.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/network.c')
-rw-r--r--src/backend/utils/adt/network.c2114
1 files changed, 2114 insertions, 0 deletions
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
new file mode 100644
index 0000000..ae11de0
--- /dev/null
+++ b/src/backend/utils/adt/network.c
@@ -0,0 +1,2114 @@
+/*
+ * PostgreSQL type definitions for the INET and CIDR types.
+ *
+ * src/backend/utils/adt/network.c
+ *
+ * Jon Postel RIP 16 Oct 1998
+ */
+
+#include "postgres.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "access/stratnum.h"
+#include "catalog/pg_opfamily.h"
+#include "catalog/pg_type.h"
+#include "common/hashfn.h"
+#include "common/ip.h"
+#include "lib/hyperloglog.h"
+#include "libpq/libpq-be.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "nodes/supportnodes.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/inet.h"
+#include "utils/lsyscache.h"
+#include "utils/sortsupport.h"
+
+
+/*
+ * An IPv4 netmask size is a value in the range of 0 - 32, which is
+ * represented with 6 bits in inet/cidr abbreviated keys where possible.
+ *
+ * An IPv4 inet/cidr abbreviated key can use up to 25 bits for subnet
+ * component.
+ */
+#define ABBREV_BITS_INET4_NETMASK_SIZE 6
+#define ABBREV_BITS_INET4_SUBNET 25
+
+/* sortsupport for inet/cidr */
+typedef struct
+{
+ int64 input_count; /* number of non-null values seen */
+ bool estimating; /* true if estimating cardinality */
+
+ hyperLogLogState abbr_card; /* cardinality estimator */
+} network_sortsupport_state;
+
+static int32 network_cmp_internal(inet *a1, inet *a2);
+static int network_fast_cmp(Datum x, Datum y, SortSupport ssup);
+static bool network_abbrev_abort(int memtupcount, SortSupport ssup);
+static Datum network_abbrev_convert(Datum original, SortSupport ssup);
+static List *match_network_function(Node *leftop,
+ Node *rightop,
+ int indexarg,
+ Oid funcid,
+ Oid opfamily);
+static List *match_network_subset(Node *leftop,
+ Node *rightop,
+ bool is_eq,
+ Oid opfamily);
+static bool addressOK(unsigned char *a, int bits, int family);
+static inet *internal_inetpl(inet *ip, int64 addend);
+
+
+/*
+ * Common INET/CIDR input routine
+ */
+static inet *
+network_in(char *src, bool is_cidr)
+{
+ int bits;
+ inet *dst;
+
+ dst = (inet *) palloc0(sizeof(inet));
+
+ /*
+ * First, check to see if this is an IPv6 or IPv4 address. IPv6 addresses
+ * will have a : somewhere in them (several, in fact) so if there is one
+ * present, assume it's V6, otherwise assume it's V4.
+ */
+
+ if (strchr(src, ':') != NULL)
+ ip_family(dst) = PGSQL_AF_INET6;
+ else
+ ip_family(dst) = PGSQL_AF_INET;
+
+ bits = pg_inet_net_pton(ip_family(dst), src, ip_addr(dst),
+ is_cidr ? ip_addrsize(dst) : -1);
+ if ((bits < 0) || (bits > ip_maxbits(dst)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ /* translator: first %s is inet or cidr */
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ is_cidr ? "cidr" : "inet", src)));
+
+ /*
+ * Error check: CIDR values must not have any bits set beyond the masklen.
+ */
+ if (is_cidr)
+ {
+ if (!addressOK(ip_addr(dst), bits, ip_family(dst)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid cidr value: \"%s\"", src),
+ errdetail("Value has bits set to right of mask.")));
+ }
+
+ ip_bits(dst) = bits;
+ SET_INET_VARSIZE(dst);
+
+ return dst;
+}
+
+Datum
+inet_in(PG_FUNCTION_ARGS)
+{
+ char *src = PG_GETARG_CSTRING(0);
+
+ PG_RETURN_INET_P(network_in(src, false));
+}
+
+Datum
+cidr_in(PG_FUNCTION_ARGS)
+{
+ char *src = PG_GETARG_CSTRING(0);
+
+ PG_RETURN_INET_P(network_in(src, true));
+}
+
+
+/*
+ * Common INET/CIDR output routine
+ */
+static char *
+network_out(inet *src, bool is_cidr)
+{
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+ char *dst;
+ int len;
+
+ dst = pg_inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src),
+ tmp, sizeof(tmp));
+ if (dst == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("could not format inet value: %m")));
+
+ /* For CIDR, add /n if not present */
+ if (is_cidr && strchr(tmp, '/') == NULL)
+ {
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
+ }
+
+ return pstrdup(tmp);
+}
+
+Datum
+inet_out(PG_FUNCTION_ARGS)
+{
+ inet *src = PG_GETARG_INET_PP(0);
+
+ PG_RETURN_CSTRING(network_out(src, false));
+}
+
+Datum
+cidr_out(PG_FUNCTION_ARGS)
+{
+ inet *src = PG_GETARG_INET_PP(0);
+
+ PG_RETURN_CSTRING(network_out(src, true));
+}
+
+
+/*
+ * network_recv - converts external binary format to inet
+ *
+ * The external representation is (one byte apiece for)
+ * family, bits, is_cidr, address length, address in network byte order.
+ *
+ * Presence of is_cidr is largely for historical reasons, though it might
+ * allow some code-sharing on the client side. We send it correctly on
+ * output, but ignore the value on input.
+ */
+static inet *
+network_recv(StringInfo buf, bool is_cidr)
+{
+ inet *addr;
+ char *addrptr;
+ int bits;
+ int nb,
+ i;
+
+ /* make sure any unused bits in a CIDR value are zeroed */
+ addr = (inet *) palloc0(sizeof(inet));
+
+ ip_family(addr) = pq_getmsgbyte(buf);
+ if (ip_family(addr) != PGSQL_AF_INET &&
+ ip_family(addr) != PGSQL_AF_INET6)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ /* translator: %s is inet or cidr */
+ errmsg("invalid address family in external \"%s\" value",
+ is_cidr ? "cidr" : "inet")));
+ bits = pq_getmsgbyte(buf);
+ if (bits < 0 || bits > ip_maxbits(addr))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ /* translator: %s is inet or cidr */
+ errmsg("invalid bits in external \"%s\" value",
+ is_cidr ? "cidr" : "inet")));
+ ip_bits(addr) = bits;
+ i = pq_getmsgbyte(buf); /* ignore is_cidr */
+ nb = pq_getmsgbyte(buf);
+ if (nb != ip_addrsize(addr))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ /* translator: %s is inet or cidr */
+ errmsg("invalid length in external \"%s\" value",
+ is_cidr ? "cidr" : "inet")));
+
+ addrptr = (char *) ip_addr(addr);
+ for (i = 0; i < nb; i++)
+ addrptr[i] = pq_getmsgbyte(buf);
+
+ /*
+ * Error check: CIDR values must not have any bits set beyond the masklen.
+ */
+ if (is_cidr)
+ {
+ if (!addressOK(ip_addr(addr), bits, ip_family(addr)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("invalid external \"cidr\" value"),
+ errdetail("Value has bits set to right of mask.")));
+ }
+
+ SET_INET_VARSIZE(addr);
+
+ return addr;
+}
+
+Datum
+inet_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+
+ PG_RETURN_INET_P(network_recv(buf, false));
+}
+
+Datum
+cidr_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+
+ PG_RETURN_INET_P(network_recv(buf, true));
+}
+
+
+/*
+ * network_send - converts inet to binary format
+ */
+static bytea *
+network_send(inet *addr, bool is_cidr)
+{
+ StringInfoData buf;
+ char *addrptr;
+ int nb,
+ i;
+
+ pq_begintypsend(&buf);
+ pq_sendbyte(&buf, ip_family(addr));
+ pq_sendbyte(&buf, ip_bits(addr));
+ pq_sendbyte(&buf, is_cidr);
+ nb = ip_addrsize(addr);
+ if (nb < 0)
+ nb = 0;
+ pq_sendbyte(&buf, nb);
+ addrptr = (char *) ip_addr(addr);
+ for (i = 0; i < nb; i++)
+ pq_sendbyte(&buf, addrptr[i]);
+ return pq_endtypsend(&buf);
+}
+
+Datum
+inet_send(PG_FUNCTION_ARGS)
+{
+ inet *addr = PG_GETARG_INET_PP(0);
+
+ PG_RETURN_BYTEA_P(network_send(addr, false));
+}
+
+Datum
+cidr_send(PG_FUNCTION_ARGS)
+{
+ inet *addr = PG_GETARG_INET_PP(0);
+
+ PG_RETURN_BYTEA_P(network_send(addr, true));
+}
+
+
+Datum
+inet_to_cidr(PG_FUNCTION_ARGS)
+{
+ inet *src = PG_GETARG_INET_PP(0);
+ int bits;
+
+ bits = ip_bits(src);
+
+ /* safety check */
+ if ((bits < 0) || (bits > ip_maxbits(src)))
+ elog(ERROR, "invalid inet bit length: %d", bits);
+
+ PG_RETURN_INET_P(cidr_set_masklen_internal(src, bits));
+}
+
+Datum
+inet_set_masklen(PG_FUNCTION_ARGS)
+{
+ inet *src = PG_GETARG_INET_PP(0);
+ int bits = PG_GETARG_INT32(1);
+ inet *dst;
+
+ if (bits == -1)
+ bits = ip_maxbits(src);
+
+ if ((bits < 0) || (bits > ip_maxbits(src)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid mask length: %d", bits)));
+
+ /* clone the original data */
+ dst = (inet *) palloc(VARSIZE_ANY(src));
+ memcpy(dst, src, VARSIZE_ANY(src));
+
+ ip_bits(dst) = bits;
+
+ PG_RETURN_INET_P(dst);
+}
+
+Datum
+cidr_set_masklen(PG_FUNCTION_ARGS)
+{
+ inet *src = PG_GETARG_INET_PP(0);
+ int bits = PG_GETARG_INT32(1);
+
+ if (bits == -1)
+ bits = ip_maxbits(src);
+
+ if ((bits < 0) || (bits > ip_maxbits(src)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid mask length: %d", bits)));
+
+ PG_RETURN_INET_P(cidr_set_masklen_internal(src, bits));
+}
+
+/*
+ * Copy src and set mask length to 'bits' (which must be valid for the family)
+ */
+inet *
+cidr_set_masklen_internal(const inet *src, int bits)
+{
+ inet *dst = (inet *) palloc0(sizeof(inet));
+
+ ip_family(dst) = ip_family(src);
+ ip_bits(dst) = bits;
+
+ if (bits > 0)
+ {
+ Assert(bits <= ip_maxbits(dst));
+
+ /* Clone appropriate bytes of the address, leaving the rest 0 */
+ memcpy(ip_addr(dst), ip_addr(src), (bits + 7) / 8);
+
+ /* Clear any unwanted bits in the last partial byte */
+ if (bits % 8)
+ ip_addr(dst)[bits / 8] &= ~(0xFF >> (bits % 8));
+ }
+
+ /* Set varlena header correctly */
+ SET_INET_VARSIZE(dst);
+
+ return dst;
+}
+
+/*
+ * Basic comparison function for sorting and inet/cidr comparisons.
+ *
+ * Comparison is first on the common bits of the network part, then on
+ * the length of the network part, and then on the whole unmasked address.
+ * The effect is that the network part is the major sort key, and for
+ * equal network parts we sort on the host part. Note this is only sane
+ * for CIDR if address bits to the right of the mask are guaranteed zero;
+ * otherwise logically-equal CIDRs might compare different.
+ */
+
+static int32
+network_cmp_internal(inet *a1, inet *a2)
+{
+ if (ip_family(a1) == ip_family(a2))
+ {
+ int order;
+
+ order = bitncmp(ip_addr(a1), ip_addr(a2),
+ Min(ip_bits(a1), ip_bits(a2)));
+ if (order != 0)
+ return order;
+ order = ((int) ip_bits(a1)) - ((int) ip_bits(a2));
+ if (order != 0)
+ return order;
+ return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
+ }
+
+ return ip_family(a1) - ip_family(a2);
+}
+
+Datum
+network_cmp(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ PG_RETURN_INT32(network_cmp_internal(a1, a2));
+}
+
+/*
+ * SortSupport strategy routine
+ */
+Datum
+network_sortsupport(PG_FUNCTION_ARGS)
+{
+ SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
+
+ ssup->comparator = network_fast_cmp;
+ ssup->ssup_extra = NULL;
+
+ if (ssup->abbreviate)
+ {
+ network_sortsupport_state *uss;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
+
+ uss = palloc(sizeof(network_sortsupport_state));
+ uss->input_count = 0;
+ uss->estimating = true;
+ initHyperLogLog(&uss->abbr_card, 10);
+
+ ssup->ssup_extra = uss;
+
+ ssup->comparator = ssup_datum_unsigned_cmp;
+ ssup->abbrev_converter = network_abbrev_convert;
+ ssup->abbrev_abort = network_abbrev_abort;
+ ssup->abbrev_full_comparator = network_fast_cmp;
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * SortSupport comparison func
+ */
+static int
+network_fast_cmp(Datum x, Datum y, SortSupport ssup)
+{
+ inet *arg1 = DatumGetInetPP(x);
+ inet *arg2 = DatumGetInetPP(y);
+
+ return network_cmp_internal(arg1, arg2);
+}
+
+/*
+ * Callback for estimating effectiveness of abbreviated key optimization.
+ *
+ * We pay no attention to the cardinality of the non-abbreviated data, because
+ * there is no equality fast-path within authoritative inet comparator.
+ */
+static bool
+network_abbrev_abort(int memtupcount, SortSupport ssup)
+{
+ network_sortsupport_state *uss = ssup->ssup_extra;
+ double abbr_card;
+
+ if (memtupcount < 10000 || uss->input_count < 10000 || !uss->estimating)
+ return false;
+
+ abbr_card = estimateHyperLogLog(&uss->abbr_card);
+
+ /*
+ * If we have >100k distinct values, then even if we were sorting many
+ * billion rows we'd likely still break even, and the penalty of undoing
+ * that many rows of abbrevs would probably not be worth it. At this point
+ * we stop counting because we know that we're now fully committed.
+ */
+ if (abbr_card > 100000.0)
+ {
+#ifdef TRACE_SORT
+ if (trace_sort)
+ elog(LOG,
+ "network_abbrev: estimation ends at cardinality %f"
+ " after " INT64_FORMAT " values (%d rows)",
+ abbr_card, uss->input_count, memtupcount);
+#endif
+ uss->estimating = false;
+ return false;
+ }
+
+ /*
+ * Target minimum cardinality is 1 per ~2k of non-null inputs. 0.5 row
+ * fudge factor allows us to abort earlier on genuinely pathological data
+ * where we've had exactly one abbreviated value in the first 2k
+ * (non-null) rows.
+ */
+ if (abbr_card < uss->input_count / 2000.0 + 0.5)
+ {
+#ifdef TRACE_SORT
+ if (trace_sort)
+ elog(LOG,
+ "network_abbrev: aborting abbreviation at cardinality %f"
+ " below threshold %f after " INT64_FORMAT " values (%d rows)",
+ abbr_card, uss->input_count / 2000.0 + 0.5, uss->input_count,
+ memtupcount);
+#endif
+ return true;
+ }
+
+#ifdef TRACE_SORT
+ if (trace_sort)
+ elog(LOG,
+ "network_abbrev: cardinality %f after " INT64_FORMAT
+ " values (%d rows)", abbr_card, uss->input_count, memtupcount);
+#endif
+
+ return false;
+}
+
+/*
+ * SortSupport conversion routine. Converts original inet/cidr representation
+ * to abbreviated key representation that works with simple 3-way unsigned int
+ * comparisons. The network_cmp_internal() rules for sorting inet/cidr datums
+ * are followed by abbreviated comparisons by an encoding scheme that
+ * conditions keys through careful use of padding.
+ *
+ * Some background: inet values have three major components (take for example
+ * the address 1.2.3.4/24):
+ *
+ * * A network, or netmasked bits (1.2.3.0).
+ * * A netmask size (/24).
+ * * A subnet, or bits outside of the netmask (0.0.0.4).
+ *
+ * cidr values are the same except that with only the first two components --
+ * all their subnet bits *must* be zero (1.2.3.0/24).
+ *
+ * IPv4 and IPv6 are identical in this makeup, with the difference being that
+ * IPv4 addresses have a maximum of 32 bits compared to IPv6's 64 bits, so in
+ * IPv6 each part may be larger.
+ *
+ * inet/cidr types compare using these sorting rules. If inequality is detected
+ * at any step, comparison is finished. If any rule is a tie, the algorithm
+ * drops through to the next to break it:
+ *
+ * 1. IPv4 always appears before IPv6.
+ * 2. Network bits are compared.
+ * 3. Netmask size is compared.
+ * 4. All bits are compared (having made it here, we know that both
+ * netmasked bits and netmask size are equal, so we're in effect only
+ * comparing subnet bits).
+ *
+ * When generating abbreviated keys for SortSupport, we pack as much as we can
+ * into a datum while ensuring that when comparing those keys as integers,
+ * these rules will be respected. Exact contents depend on IP family and datum
+ * size.
+ *
+ * IPv4
+ * ----
+ *
+ * 4 byte datums:
+ *
+ * Start with 1 bit for the IP family (IPv4 or IPv6; this bit is present in
+ * every case below) followed by all but 1 of the netmasked bits.
+ *
+ * +----------+---------------------+
+ * | 1 bit IP | 31 bits network | (1 bit network
+ * | family | (truncated) | omitted)
+ * +----------+---------------------+
+ *
+ * 8 byte datums:
+ *
+ * We have space to store all netmasked bits, followed by the netmask size,
+ * followed by 25 bits of the subnet (25 bits is usually more than enough in
+ * practice). cidr datums always have all-zero subnet bits.
+ *
+ * +----------+-----------------------+--------------+--------------------+
+ * | 1 bit IP | 32 bits network | 6 bits | 25 bits subnet |
+ * | family | (full) | network size | (truncated) |
+ * +----------+-----------------------+--------------+--------------------+
+ *
+ * IPv6
+ * ----
+ *
+ * 4 byte datums:
+ *
+ * +----------+---------------------+
+ * | 1 bit IP | 31 bits network | (up to 97 bits
+ * | family | (truncated) | network omitted)
+ * +----------+---------------------+
+ *
+ * 8 byte datums:
+ *
+ * +----------+---------------------------------+
+ * | 1 bit IP | 63 bits network | (up to 65 bits
+ * | family | (truncated) | network omitted)
+ * +----------+---------------------------------+
+ */
+static Datum
+network_abbrev_convert(Datum original, SortSupport ssup)
+{
+ network_sortsupport_state *uss = ssup->ssup_extra;
+ inet *authoritative = DatumGetInetPP(original);
+ Datum res,
+ ipaddr_datum,
+ subnet_bitmask,
+ network;
+ int subnet_size;
+
+ Assert(ip_family(authoritative) == PGSQL_AF_INET ||
+ ip_family(authoritative) == PGSQL_AF_INET6);
+
+ /*
+ * Get an unsigned integer representation of the IP address by taking its
+ * first 4 or 8 bytes. Always take all 4 bytes of an IPv4 address. Take
+ * the first 8 bytes of an IPv6 address with an 8 byte datum and 4 bytes
+ * otherwise.
+ *
+ * We're consuming an array of unsigned char, so byteswap on little endian
+ * systems (an inet's ipaddr field stores the most significant byte
+ * first).
+ */
+ if (ip_family(authoritative) == PGSQL_AF_INET)
+ {
+ uint32 ipaddr_datum32;
+
+ memcpy(&ipaddr_datum32, ip_addr(authoritative), sizeof(uint32));
+
+ /* Must byteswap on little-endian machines */
+#ifndef WORDS_BIGENDIAN
+ ipaddr_datum = pg_bswap32(ipaddr_datum32);
+#else
+ ipaddr_datum = ipaddr_datum32;
+#endif
+
+ /* Initialize result without setting ipfamily bit */
+ res = (Datum) 0;
+ }
+ else
+ {
+ memcpy(&ipaddr_datum, ip_addr(authoritative), sizeof(Datum));
+
+ /* Must byteswap on little-endian machines */
+ ipaddr_datum = DatumBigEndianToNative(ipaddr_datum);
+
+ /* Initialize result with ipfamily (most significant) bit set */
+ res = ((Datum) 1) << (SIZEOF_DATUM * BITS_PER_BYTE - 1);
+ }
+
+ /*
+ * ipaddr_datum must be "split": high order bits go in "network" component
+ * of abbreviated key (often with zeroed bits at the end due to masking),
+ * while low order bits go in "subnet" component when there is space for
+ * one. This is often accomplished by generating a temp datum subnet
+ * bitmask, which we may reuse later when generating the subnet bits
+ * themselves. (Note that subnet bits are only used with IPv4 datums on
+ * platforms where datum is 8 bytes.)
+ *
+ * The number of bits in subnet is used to generate a datum subnet
+ * bitmask. For example, with a /24 IPv4 datum there are 8 subnet bits
+ * (since 32 - 24 is 8), so the final subnet bitmask is B'1111 1111'. We
+ * need explicit handling for cases where the ipaddr bits cannot all fit
+ * in a datum, though (otherwise we'd incorrectly mask the network
+ * component with IPv6 values).
+ */
+ subnet_size = ip_maxbits(authoritative) - ip_bits(authoritative);
+ Assert(subnet_size >= 0);
+ /* subnet size must work with prefix ipaddr cases */
+ subnet_size %= SIZEOF_DATUM * BITS_PER_BYTE;
+ if (ip_bits(authoritative) == 0)
+ {
+ /* Fit as many ipaddr bits as possible into subnet */
+ subnet_bitmask = ((Datum) 0) - 1;
+ network = 0;
+ }
+ else if (ip_bits(authoritative) < SIZEOF_DATUM * BITS_PER_BYTE)
+ {
+ /* Split ipaddr bits between network and subnet */
+ subnet_bitmask = (((Datum) 1) << subnet_size) - 1;
+ network = ipaddr_datum & ~subnet_bitmask;
+ }
+ else
+ {
+ /* Fit as many ipaddr bits as possible into network */
+ subnet_bitmask = 0;
+ network = ipaddr_datum;
+ }
+
+#if SIZEOF_DATUM == 8
+ if (ip_family(authoritative) == PGSQL_AF_INET)
+ {
+ /*
+ * IPv4 with 8 byte datums: keep all 32 netmasked bits, netmask size,
+ * and most significant 25 subnet bits
+ */
+ Datum netmask_size = (Datum) ip_bits(authoritative);
+ Datum subnet;
+
+ /*
+ * Shift left 31 bits: 6 bits netmask size + 25 subnet bits.
+ *
+ * We don't make any distinction between network bits that are zero
+ * due to masking and "true"/non-masked zero bits. An abbreviated
+ * comparison that is resolved by comparing a non-masked and non-zero
+ * bit to a masked/zeroed bit is effectively resolved based on
+ * ip_bits(), even though the comparison won't reach the netmask_size
+ * bits.
+ */
+ network <<= (ABBREV_BITS_INET4_NETMASK_SIZE +
+ ABBREV_BITS_INET4_SUBNET);
+
+ /* Shift size to make room for subnet bits at the end */
+ netmask_size <<= ABBREV_BITS_INET4_SUBNET;
+
+ /* Extract subnet bits without shifting them */
+ subnet = ipaddr_datum & subnet_bitmask;
+
+ /*
+ * If we have more than 25 subnet bits, we can't fit everything. Shift
+ * subnet down to avoid clobbering bits that are only supposed to be
+ * used for netmask_size.
+ *
+ * Discarding the least significant subnet bits like this is correct
+ * because abbreviated comparisons that are resolved at the subnet
+ * level must have had equal netmask_size/ip_bits() values in order to
+ * get that far.
+ */
+ if (subnet_size > ABBREV_BITS_INET4_SUBNET)
+ subnet >>= subnet_size - ABBREV_BITS_INET4_SUBNET;
+
+ /*
+ * Assemble the final abbreviated key without clobbering the ipfamily
+ * bit that must remain a zero.
+ */
+ res |= network | netmask_size | subnet;
+ }
+ else
+#endif
+ {
+ /*
+ * 4 byte datums, or IPv6 with 8 byte datums: Use as many of the
+ * netmasked bits as will fit in final abbreviated key. Avoid
+ * clobbering the ipfamily bit that was set earlier.
+ */
+ res |= network >> 1;
+ }
+
+ uss->input_count += 1;
+
+ /* Hash abbreviated key */
+ if (uss->estimating)
+ {
+ uint32 tmp;
+
+#if SIZEOF_DATUM == 8
+ tmp = (uint32) res ^ (uint32) ((uint64) res >> 32);
+#else /* SIZEOF_DATUM != 8 */
+ tmp = (uint32) res;
+#endif
+
+ addHyperLogLog(&uss->abbr_card, DatumGetUInt32(hash_uint32(tmp)));
+ }
+
+ return res;
+}
+
+/*
+ * Boolean ordering tests.
+ */
+Datum
+network_lt(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ PG_RETURN_BOOL(network_cmp_internal(a1, a2) < 0);
+}
+
+Datum
+network_le(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ PG_RETURN_BOOL(network_cmp_internal(a1, a2) <= 0);
+}
+
+Datum
+network_eq(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ PG_RETURN_BOOL(network_cmp_internal(a1, a2) == 0);
+}
+
+Datum
+network_ge(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ PG_RETURN_BOOL(network_cmp_internal(a1, a2) >= 0);
+}
+
+Datum
+network_gt(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ PG_RETURN_BOOL(network_cmp_internal(a1, a2) > 0);
+}
+
+Datum
+network_ne(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ PG_RETURN_BOOL(network_cmp_internal(a1, a2) != 0);
+}
+
+/*
+ * MIN/MAX support functions.
+ */
+Datum
+network_smaller(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ if (network_cmp_internal(a1, a2) < 0)
+ PG_RETURN_INET_P(a1);
+ else
+ PG_RETURN_INET_P(a2);
+}
+
+Datum
+network_larger(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ if (network_cmp_internal(a1, a2) > 0)
+ PG_RETURN_INET_P(a1);
+ else
+ PG_RETURN_INET_P(a2);
+}
+
+/*
+ * Support function for hash indexes on inet/cidr.
+ */
+Datum
+hashinet(PG_FUNCTION_ARGS)
+{
+ inet *addr = PG_GETARG_INET_PP(0);
+ int addrsize = ip_addrsize(addr);
+
+ /* XXX this assumes there are no pad bytes in the data structure */
+ return hash_any((unsigned char *) VARDATA_ANY(addr), addrsize + 2);
+}
+
+Datum
+hashinetextended(PG_FUNCTION_ARGS)
+{
+ inet *addr = PG_GETARG_INET_PP(0);
+ int addrsize = ip_addrsize(addr);
+
+ return hash_any_extended((unsigned char *) VARDATA_ANY(addr), addrsize + 2,
+ PG_GETARG_INT64(1));
+}
+
+/*
+ * Boolean network-inclusion tests.
+ */
+Datum
+network_sub(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ if (ip_family(a1) == ip_family(a2))
+ {
+ PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2) &&
+ bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
+ }
+
+ PG_RETURN_BOOL(false);
+}
+
+Datum
+network_subeq(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ if (ip_family(a1) == ip_family(a2))
+ {
+ PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2) &&
+ bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
+ }
+
+ PG_RETURN_BOOL(false);
+}
+
+Datum
+network_sup(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ if (ip_family(a1) == ip_family(a2))
+ {
+ PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2) &&
+ bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
+ }
+
+ PG_RETURN_BOOL(false);
+}
+
+Datum
+network_supeq(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ if (ip_family(a1) == ip_family(a2))
+ {
+ PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2) &&
+ bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
+ }
+
+ PG_RETURN_BOOL(false);
+}
+
+Datum
+network_overlap(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ if (ip_family(a1) == ip_family(a2))
+ {
+ PG_RETURN_BOOL(bitncmp(ip_addr(a1), ip_addr(a2),
+ Min(ip_bits(a1), ip_bits(a2))) == 0);
+ }
+
+ PG_RETURN_BOOL(false);
+}
+
+/*
+ * Planner support function for network subset/superset operators
+ */
+Datum
+network_subset_support(PG_FUNCTION_ARGS)
+{
+ Node *rawreq = (Node *) PG_GETARG_POINTER(0);
+ Node *ret = NULL;
+
+ if (IsA(rawreq, SupportRequestIndexCondition))
+ {
+ /* Try to convert operator/function call to index conditions */
+ SupportRequestIndexCondition *req = (SupportRequestIndexCondition *) rawreq;
+
+ if (is_opclause(req->node))
+ {
+ OpExpr *clause = (OpExpr *) req->node;
+
+ Assert(list_length(clause->args) == 2);
+ ret = (Node *)
+ match_network_function((Node *) linitial(clause->args),
+ (Node *) lsecond(clause->args),
+ req->indexarg,
+ req->funcid,
+ req->opfamily);
+ }
+ else if (is_funcclause(req->node)) /* be paranoid */
+ {
+ FuncExpr *clause = (FuncExpr *) req->node;
+
+ Assert(list_length(clause->args) == 2);
+ ret = (Node *)
+ match_network_function((Node *) linitial(clause->args),
+ (Node *) lsecond(clause->args),
+ req->indexarg,
+ req->funcid,
+ req->opfamily);
+ }
+ }
+
+ PG_RETURN_POINTER(ret);
+}
+
+/*
+ * match_network_function
+ * Try to generate an indexqual for a network subset/superset function.
+ *
+ * This layer is just concerned with identifying the function and swapping
+ * the arguments if necessary.
+ */
+static List *
+match_network_function(Node *leftop,
+ Node *rightop,
+ int indexarg,
+ Oid funcid,
+ Oid opfamily)
+{
+ switch (funcid)
+ {
+ case F_NETWORK_SUB:
+ /* indexkey must be on the left */
+ if (indexarg != 0)
+ return NIL;
+ return match_network_subset(leftop, rightop, false, opfamily);
+
+ case F_NETWORK_SUBEQ:
+ /* indexkey must be on the left */
+ if (indexarg != 0)
+ return NIL;
+ return match_network_subset(leftop, rightop, true, opfamily);
+
+ case F_NETWORK_SUP:
+ /* indexkey must be on the right */
+ if (indexarg != 1)
+ return NIL;
+ return match_network_subset(rightop, leftop, false, opfamily);
+
+ case F_NETWORK_SUPEQ:
+ /* indexkey must be on the right */
+ if (indexarg != 1)
+ return NIL;
+ return match_network_subset(rightop, leftop, true, opfamily);
+
+ default:
+
+ /*
+ * We'd only get here if somebody attached this support function
+ * to an unexpected function. Maybe we should complain, but for
+ * now, do nothing.
+ */
+ return NIL;
+ }
+}
+
+/*
+ * match_network_subset
+ * Try to generate an indexqual for a network subset function.
+ */
+static List *
+match_network_subset(Node *leftop,
+ Node *rightop,
+ bool is_eq,
+ Oid opfamily)
+{
+ List *result;
+ Datum rightopval;
+ Oid datatype = INETOID;
+ Oid opr1oid;
+ Oid opr2oid;
+ Datum opr1right;
+ Datum opr2right;
+ Expr *expr;
+
+ /*
+ * Can't do anything with a non-constant or NULL comparison value.
+ *
+ * Note that since we restrict ourselves to cases with a hard constant on
+ * the RHS, it's a-fortiori a pseudoconstant, and we don't need to worry
+ * about verifying that.
+ */
+ if (!IsA(rightop, Const) ||
+ ((Const *) rightop)->constisnull)
+ return NIL;
+ rightopval = ((Const *) rightop)->constvalue;
+
+ /*
+ * Must check that index's opfamily supports the operators we will want to
+ * apply.
+ *
+ * We insist on the opfamily being the specific one we expect, else we'd
+ * do the wrong thing if someone were to make a reverse-sort opfamily with
+ * the same operators.
+ */
+ if (opfamily != NETWORK_BTREE_FAM_OID)
+ return NIL;
+
+ /*
+ * create clause "key >= network_scan_first( rightopval )", or ">" if the
+ * operator disallows equality.
+ *
+ * Note: seeing that this function supports only fixed values for opfamily
+ * and datatype, we could just hard-wire the operator OIDs instead of
+ * looking them up. But for now it seems better to be general.
+ */
+ if (is_eq)
+ {
+ opr1oid = get_opfamily_member(opfamily, datatype, datatype,
+ BTGreaterEqualStrategyNumber);
+ if (opr1oid == InvalidOid)
+ elog(ERROR, "no >= operator for opfamily %u", opfamily);
+ }
+ else
+ {
+ opr1oid = get_opfamily_member(opfamily, datatype, datatype,
+ BTGreaterStrategyNumber);
+ if (opr1oid == InvalidOid)
+ elog(ERROR, "no > operator for opfamily %u", opfamily);
+ }
+
+ opr1right = network_scan_first(rightopval);
+
+ expr = make_opclause(opr1oid, BOOLOID, false,
+ (Expr *) leftop,
+ (Expr *) makeConst(datatype, -1,
+ InvalidOid, /* not collatable */
+ -1, opr1right,
+ false, false),
+ InvalidOid, InvalidOid);
+ result = list_make1(expr);
+
+ /* create clause "key <= network_scan_last( rightopval )" */
+
+ opr2oid = get_opfamily_member(opfamily, datatype, datatype,
+ BTLessEqualStrategyNumber);
+ if (opr2oid == InvalidOid)
+ elog(ERROR, "no <= operator for opfamily %u", opfamily);
+
+ opr2right = network_scan_last(rightopval);
+
+ expr = make_opclause(opr2oid, BOOLOID, false,
+ (Expr *) leftop,
+ (Expr *) makeConst(datatype, -1,
+ InvalidOid, /* not collatable */
+ -1, opr2right,
+ false, false),
+ InvalidOid, InvalidOid);
+ result = lappend(result, expr);
+
+ return result;
+}
+
+
+/*
+ * Extract data from a network datatype.
+ */
+Datum
+network_host(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ char *ptr;
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+ /* force display of max bits, regardless of masklen... */
+ if (pg_inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
+ tmp, sizeof(tmp)) == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("could not format inet value: %m")));
+
+ /* Suppress /n if present (shouldn't happen now) */
+ if ((ptr = strchr(tmp, '/')) != NULL)
+ *ptr = '\0';
+
+ PG_RETURN_TEXT_P(cstring_to_text(tmp));
+}
+
+/*
+ * network_show implements the inet and cidr casts to text. This is not
+ * quite the same behavior as network_out, hence we can't drop it in favor
+ * of CoerceViaIO.
+ */
+Datum
+network_show(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ int len;
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+ if (pg_inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
+ tmp, sizeof(tmp)) == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("could not format inet value: %m")));
+
+ /* Add /n if not present (which it won't be) */
+ if (strchr(tmp, '/') == NULL)
+ {
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
+ }
+
+ PG_RETURN_TEXT_P(cstring_to_text(tmp));
+}
+
+Datum
+inet_abbrev(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ char *dst;
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+ dst = pg_inet_net_ntop(ip_family(ip), ip_addr(ip),
+ ip_bits(ip), tmp, sizeof(tmp));
+
+ if (dst == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("could not format inet value: %m")));
+
+ PG_RETURN_TEXT_P(cstring_to_text(tmp));
+}
+
+Datum
+cidr_abbrev(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ char *dst;
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+ dst = pg_inet_cidr_ntop(ip_family(ip), ip_addr(ip),
+ ip_bits(ip), tmp, sizeof(tmp));
+
+ if (dst == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("could not format cidr value: %m")));
+
+ PG_RETURN_TEXT_P(cstring_to_text(tmp));
+}
+
+Datum
+network_masklen(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+
+ PG_RETURN_INT32(ip_bits(ip));
+}
+
+Datum
+network_family(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+
+ switch (ip_family(ip))
+ {
+ case PGSQL_AF_INET:
+ PG_RETURN_INT32(4);
+ break;
+ case PGSQL_AF_INET6:
+ PG_RETURN_INT32(6);
+ break;
+ default:
+ PG_RETURN_INT32(0);
+ break;
+ }
+}
+
+Datum
+network_broadcast(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ inet *dst;
+ int byte;
+ int bits;
+ int maxbytes;
+ unsigned char mask;
+ unsigned char *a,
+ *b;
+
+ /* make sure any unused bits are zeroed */
+ dst = (inet *) palloc0(sizeof(inet));
+
+ maxbytes = ip_addrsize(ip);
+ bits = ip_bits(ip);
+ a = ip_addr(ip);
+ b = ip_addr(dst);
+
+ for (byte = 0; byte < maxbytes; byte++)
+ {
+ if (bits >= 8)
+ {
+ mask = 0x00;
+ bits -= 8;
+ }
+ else if (bits == 0)
+ mask = 0xff;
+ else
+ {
+ mask = 0xff >> bits;
+ bits = 0;
+ }
+
+ b[byte] = a[byte] | mask;
+ }
+
+ ip_family(dst) = ip_family(ip);
+ ip_bits(dst) = ip_bits(ip);
+ SET_INET_VARSIZE(dst);
+
+ PG_RETURN_INET_P(dst);
+}
+
+Datum
+network_network(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ inet *dst;
+ int byte;
+ int bits;
+ unsigned char mask;
+ unsigned char *a,
+ *b;
+
+ /* make sure any unused bits are zeroed */
+ dst = (inet *) palloc0(sizeof(inet));
+
+ bits = ip_bits(ip);
+ a = ip_addr(ip);
+ b = ip_addr(dst);
+
+ byte = 0;
+
+ while (bits)
+ {
+ if (bits >= 8)
+ {
+ mask = 0xff;
+ bits -= 8;
+ }
+ else
+ {
+ mask = 0xff << (8 - bits);
+ bits = 0;
+ }
+
+ b[byte] = a[byte] & mask;
+ byte++;
+ }
+
+ ip_family(dst) = ip_family(ip);
+ ip_bits(dst) = ip_bits(ip);
+ SET_INET_VARSIZE(dst);
+
+ PG_RETURN_INET_P(dst);
+}
+
+Datum
+network_netmask(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ inet *dst;
+ int byte;
+ int bits;
+ unsigned char mask;
+ unsigned char *b;
+
+ /* make sure any unused bits are zeroed */
+ dst = (inet *) palloc0(sizeof(inet));
+
+ bits = ip_bits(ip);
+ b = ip_addr(dst);
+
+ byte = 0;
+
+ while (bits)
+ {
+ if (bits >= 8)
+ {
+ mask = 0xff;
+ bits -= 8;
+ }
+ else
+ {
+ mask = 0xff << (8 - bits);
+ bits = 0;
+ }
+
+ b[byte] = mask;
+ byte++;
+ }
+
+ ip_family(dst) = ip_family(ip);
+ ip_bits(dst) = ip_maxbits(ip);
+ SET_INET_VARSIZE(dst);
+
+ PG_RETURN_INET_P(dst);
+}
+
+Datum
+network_hostmask(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ inet *dst;
+ int byte;
+ int bits;
+ int maxbytes;
+ unsigned char mask;
+ unsigned char *b;
+
+ /* make sure any unused bits are zeroed */
+ dst = (inet *) palloc0(sizeof(inet));
+
+ maxbytes = ip_addrsize(ip);
+ bits = ip_maxbits(ip) - ip_bits(ip);
+ b = ip_addr(dst);
+
+ byte = maxbytes - 1;
+
+ while (bits)
+ {
+ if (bits >= 8)
+ {
+ mask = 0xff;
+ bits -= 8;
+ }
+ else
+ {
+ mask = 0xff >> (8 - bits);
+ bits = 0;
+ }
+
+ b[byte] = mask;
+ byte--;
+ }
+
+ ip_family(dst) = ip_family(ip);
+ ip_bits(dst) = ip_maxbits(ip);
+ SET_INET_VARSIZE(dst);
+
+ PG_RETURN_INET_P(dst);
+}
+
+/*
+ * Returns true if the addresses are from the same family, or false. Used to
+ * check that we can create a network which contains both of the networks.
+ */
+Datum
+inet_same_family(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0);
+ inet *a2 = PG_GETARG_INET_PP(1);
+
+ PG_RETURN_BOOL(ip_family(a1) == ip_family(a2));
+}
+
+/*
+ * Returns the smallest CIDR which contains both of the inputs.
+ */
+Datum
+inet_merge(PG_FUNCTION_ARGS)
+{
+ inet *a1 = PG_GETARG_INET_PP(0),
+ *a2 = PG_GETARG_INET_PP(1);
+ int commonbits;
+
+ if (ip_family(a1) != ip_family(a2))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot merge addresses from different families")));
+
+ commonbits = bitncommon(ip_addr(a1), ip_addr(a2),
+ Min(ip_bits(a1), ip_bits(a2)));
+
+ PG_RETURN_INET_P(cidr_set_masklen_internal(a1, commonbits));
+}
+
+/*
+ * Convert a value of a network datatype to an approximate scalar value.
+ * This is used for estimating selectivities of inequality operators
+ * involving network types.
+ *
+ * On failure (e.g., unsupported typid), set *failure to true;
+ * otherwise, that variable is not changed.
+ */
+double
+convert_network_to_scalar(Datum value, Oid typid, bool *failure)
+{
+ switch (typid)
+ {
+ case INETOID:
+ case CIDROID:
+ {
+ inet *ip = DatumGetInetPP(value);
+ int len;
+ double res;
+ int i;
+
+ /*
+ * Note that we don't use the full address for IPv6.
+ */
+ if (ip_family(ip) == PGSQL_AF_INET)
+ len = 4;
+ else
+ len = 5;
+
+ res = ip_family(ip);
+ for (i = 0; i < len; i++)
+ {
+ res *= 256;
+ res += ip_addr(ip)[i];
+ }
+ return res;
+ }
+ case MACADDROID:
+ {
+ macaddr *mac = DatumGetMacaddrP(value);
+ double res;
+
+ res = (mac->a << 16) | (mac->b << 8) | (mac->c);
+ res *= 256 * 256 * 256;
+ res += (mac->d << 16) | (mac->e << 8) | (mac->f);
+ return res;
+ }
+ case MACADDR8OID:
+ {
+ macaddr8 *mac = DatumGetMacaddr8P(value);
+ double res;
+
+ res = (mac->a << 24) | (mac->b << 16) | (mac->c << 8) | (mac->d);
+ res *= ((double) 256) * 256 * 256 * 256;
+ res += (mac->e << 24) | (mac->f << 16) | (mac->g << 8) | (mac->h);
+ return res;
+ }
+ }
+
+ *failure = true;
+ return 0;
+}
+
+/*
+ * int
+ * bitncmp(l, r, n)
+ * compare bit masks l and r, for n bits.
+ * return:
+ * <0, >0, or 0 in the libc tradition.
+ * note:
+ * network byte order assumed. this means 192.5.5.240/28 has
+ * 0x11110000 in its fourth octet.
+ * author:
+ * Paul Vixie (ISC), June 1996
+ */
+int
+bitncmp(const unsigned char *l, const unsigned char *r, int n)
+{
+ unsigned int lb,
+ rb;
+ int x,
+ b;
+
+ b = n / 8;
+ x = memcmp(l, r, b);
+ if (x || (n % 8) == 0)
+ return x;
+
+ lb = l[b];
+ rb = r[b];
+ for (b = n % 8; b > 0; b--)
+ {
+ if (IS_HIGHBIT_SET(lb) != IS_HIGHBIT_SET(rb))
+ {
+ if (IS_HIGHBIT_SET(lb))
+ return 1;
+ return -1;
+ }
+ lb <<= 1;
+ rb <<= 1;
+ }
+ return 0;
+}
+
+/*
+ * bitncommon: compare bit masks l and r, for up to n bits.
+ *
+ * Returns the number of leading bits that match (0 to n).
+ */
+int
+bitncommon(const unsigned char *l, const unsigned char *r, int n)
+{
+ int byte,
+ nbits;
+
+ /* number of bits to examine in last byte */
+ nbits = n % 8;
+
+ /* check whole bytes */
+ for (byte = 0; byte < n / 8; byte++)
+ {
+ if (l[byte] != r[byte])
+ {
+ /* at least one bit in the last byte is not common */
+ nbits = 7;
+ break;
+ }
+ }
+
+ /* check bits in last partial byte */
+ if (nbits != 0)
+ {
+ /* calculate diff of first non-matching bytes */
+ unsigned int diff = l[byte] ^ r[byte];
+
+ /* compare the bits from the most to the least */
+ while ((diff >> (8 - nbits)) != 0)
+ nbits--;
+ }
+
+ return (8 * byte) + nbits;
+}
+
+
+/*
+ * Verify a CIDR address is OK (doesn't have bits set past the masklen)
+ */
+static bool
+addressOK(unsigned char *a, int bits, int family)
+{
+ int byte;
+ int nbits;
+ int maxbits;
+ int maxbytes;
+ unsigned char mask;
+
+ if (family == PGSQL_AF_INET)
+ {
+ maxbits = 32;
+ maxbytes = 4;
+ }
+ else
+ {
+ maxbits = 128;
+ maxbytes = 16;
+ }
+ Assert(bits <= maxbits);
+
+ if (bits == maxbits)
+ return true;
+
+ byte = bits / 8;
+
+ nbits = bits % 8;
+ mask = 0xff;
+ if (bits != 0)
+ mask >>= nbits;
+
+ while (byte < maxbytes)
+ {
+ if ((a[byte] & mask) != 0)
+ return false;
+ mask = 0xff;
+ byte++;
+ }
+
+ return true;
+}
+
+
+/*
+ * These functions are used by planner to generate indexscan limits
+ * for clauses a << b and a <<= b
+ */
+
+/* return the minimal value for an IP on a given network */
+Datum
+network_scan_first(Datum in)
+{
+ return DirectFunctionCall1(network_network, in);
+}
+
+/*
+ * return "last" IP on a given network. It's the broadcast address,
+ * however, masklen has to be set to its max bits, since
+ * 192.168.0.255/24 is considered less than 192.168.0.255/32
+ *
+ * inet_set_masklen() hacked to max out the masklength to 128 for IPv6
+ * and 32 for IPv4 when given '-1' as argument.
+ */
+Datum
+network_scan_last(Datum in)
+{
+ return DirectFunctionCall2(inet_set_masklen,
+ DirectFunctionCall1(network_broadcast, in),
+ Int32GetDatum(-1));
+}
+
+
+/*
+ * IP address that the client is connecting from (NULL if Unix socket)
+ */
+Datum
+inet_client_addr(PG_FUNCTION_ARGS)
+{
+ Port *port = MyProcPort;
+ char remote_host[NI_MAXHOST];
+ int ret;
+
+ if (port == NULL)
+ PG_RETURN_NULL();
+
+ switch (port->raddr.addr.ss_family)
+ {
+ case AF_INET:
+#ifdef HAVE_IPV6
+ case AF_INET6:
+#endif
+ break;
+ default:
+ PG_RETURN_NULL();
+ }
+
+ remote_host[0] = '\0';
+
+ ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
+ remote_host, sizeof(remote_host),
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (ret != 0)
+ PG_RETURN_NULL();
+
+ clean_ipv6_addr(port->raddr.addr.ss_family, remote_host);
+
+ PG_RETURN_INET_P(network_in(remote_host, false));
+}
+
+
+/*
+ * port that the client is connecting from (NULL if Unix socket)
+ */
+Datum
+inet_client_port(PG_FUNCTION_ARGS)
+{
+ Port *port = MyProcPort;
+ char remote_port[NI_MAXSERV];
+ int ret;
+
+ if (port == NULL)
+ PG_RETURN_NULL();
+
+ switch (port->raddr.addr.ss_family)
+ {
+ case AF_INET:
+#ifdef HAVE_IPV6
+ case AF_INET6:
+#endif
+ break;
+ default:
+ PG_RETURN_NULL();
+ }
+
+ remote_port[0] = '\0';
+
+ ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
+ NULL, 0,
+ remote_port, sizeof(remote_port),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (ret != 0)
+ PG_RETURN_NULL();
+
+ PG_RETURN_DATUM(DirectFunctionCall1(int4in, CStringGetDatum(remote_port)));
+}
+
+
+/*
+ * IP address that the server accepted the connection on (NULL if Unix socket)
+ */
+Datum
+inet_server_addr(PG_FUNCTION_ARGS)
+{
+ Port *port = MyProcPort;
+ char local_host[NI_MAXHOST];
+ int ret;
+
+ if (port == NULL)
+ PG_RETURN_NULL();
+
+ switch (port->laddr.addr.ss_family)
+ {
+ case AF_INET:
+#ifdef HAVE_IPV6
+ case AF_INET6:
+#endif
+ break;
+ default:
+ PG_RETURN_NULL();
+ }
+
+ local_host[0] = '\0';
+
+ ret = pg_getnameinfo_all(&port->laddr.addr, port->laddr.salen,
+ local_host, sizeof(local_host),
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (ret != 0)
+ PG_RETURN_NULL();
+
+ clean_ipv6_addr(port->laddr.addr.ss_family, local_host);
+
+ PG_RETURN_INET_P(network_in(local_host, false));
+}
+
+
+/*
+ * port that the server accepted the connection on (NULL if Unix socket)
+ */
+Datum
+inet_server_port(PG_FUNCTION_ARGS)
+{
+ Port *port = MyProcPort;
+ char local_port[NI_MAXSERV];
+ int ret;
+
+ if (port == NULL)
+ PG_RETURN_NULL();
+
+ switch (port->laddr.addr.ss_family)
+ {
+ case AF_INET:
+#ifdef HAVE_IPV6
+ case AF_INET6:
+#endif
+ break;
+ default:
+ PG_RETURN_NULL();
+ }
+
+ local_port[0] = '\0';
+
+ ret = pg_getnameinfo_all(&port->laddr.addr, port->laddr.salen,
+ NULL, 0,
+ local_port, sizeof(local_port),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (ret != 0)
+ PG_RETURN_NULL();
+
+ PG_RETURN_DATUM(DirectFunctionCall1(int4in, CStringGetDatum(local_port)));
+}
+
+
+Datum
+inetnot(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ inet *dst;
+
+ dst = (inet *) palloc0(sizeof(inet));
+
+ {
+ int nb = ip_addrsize(ip);
+ unsigned char *pip = ip_addr(ip);
+ unsigned char *pdst = ip_addr(dst);
+
+ while (--nb >= 0)
+ pdst[nb] = ~pip[nb];
+ }
+ ip_bits(dst) = ip_bits(ip);
+
+ ip_family(dst) = ip_family(ip);
+ SET_INET_VARSIZE(dst);
+
+ PG_RETURN_INET_P(dst);
+}
+
+
+Datum
+inetand(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ inet *ip2 = PG_GETARG_INET_PP(1);
+ inet *dst;
+
+ dst = (inet *) palloc0(sizeof(inet));
+
+ if (ip_family(ip) != ip_family(ip2))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot AND inet values of different sizes")));
+ else
+ {
+ int nb = ip_addrsize(ip);
+ unsigned char *pip = ip_addr(ip);
+ unsigned char *pip2 = ip_addr(ip2);
+ unsigned char *pdst = ip_addr(dst);
+
+ while (--nb >= 0)
+ pdst[nb] = pip[nb] & pip2[nb];
+ }
+ ip_bits(dst) = Max(ip_bits(ip), ip_bits(ip2));
+
+ ip_family(dst) = ip_family(ip);
+ SET_INET_VARSIZE(dst);
+
+ PG_RETURN_INET_P(dst);
+}
+
+
+Datum
+inetor(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ inet *ip2 = PG_GETARG_INET_PP(1);
+ inet *dst;
+
+ dst = (inet *) palloc0(sizeof(inet));
+
+ if (ip_family(ip) != ip_family(ip2))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot OR inet values of different sizes")));
+ else
+ {
+ int nb = ip_addrsize(ip);
+ unsigned char *pip = ip_addr(ip);
+ unsigned char *pip2 = ip_addr(ip2);
+ unsigned char *pdst = ip_addr(dst);
+
+ while (--nb >= 0)
+ pdst[nb] = pip[nb] | pip2[nb];
+ }
+ ip_bits(dst) = Max(ip_bits(ip), ip_bits(ip2));
+
+ ip_family(dst) = ip_family(ip);
+ SET_INET_VARSIZE(dst);
+
+ PG_RETURN_INET_P(dst);
+}
+
+
+static inet *
+internal_inetpl(inet *ip, int64 addend)
+{
+ inet *dst;
+
+ dst = (inet *) palloc0(sizeof(inet));
+
+ {
+ int nb = ip_addrsize(ip);
+ unsigned char *pip = ip_addr(ip);
+ unsigned char *pdst = ip_addr(dst);
+ int carry = 0;
+
+ while (--nb >= 0)
+ {
+ carry = pip[nb] + (int) (addend & 0xFF) + carry;
+ pdst[nb] = (unsigned char) (carry & 0xFF);
+ carry >>= 8;
+
+ /*
+ * We have to be careful about right-shifting addend because
+ * right-shift isn't portable for negative values, while simply
+ * dividing by 256 doesn't work (the standard rounding is in the
+ * wrong direction, besides which there may be machines out there
+ * that round the wrong way). So, explicitly clear the low-order
+ * byte to remove any doubt about the correct result of the
+ * division, and then divide rather than shift.
+ */
+ addend &= ~((int64) 0xFF);
+ addend /= 0x100;
+ }
+
+ /*
+ * At this point we should have addend and carry both zero if original
+ * addend was >= 0, or addend -1 and carry 1 if original addend was <
+ * 0. Anything else means overflow.
+ */
+ if (!((addend == 0 && carry == 0) ||
+ (addend == -1 && carry == 1)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("result is out of range")));
+ }
+
+ ip_bits(dst) = ip_bits(ip);
+ ip_family(dst) = ip_family(ip);
+ SET_INET_VARSIZE(dst);
+
+ return dst;
+}
+
+
+Datum
+inetpl(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ int64 addend = PG_GETARG_INT64(1);
+
+ PG_RETURN_INET_P(internal_inetpl(ip, addend));
+}
+
+
+Datum
+inetmi_int8(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ int64 addend = PG_GETARG_INT64(1);
+
+ PG_RETURN_INET_P(internal_inetpl(ip, -addend));
+}
+
+
+Datum
+inetmi(PG_FUNCTION_ARGS)
+{
+ inet *ip = PG_GETARG_INET_PP(0);
+ inet *ip2 = PG_GETARG_INET_PP(1);
+ int64 res = 0;
+
+ if (ip_family(ip) != ip_family(ip2))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot subtract inet values of different sizes")));
+ else
+ {
+ /*
+ * We form the difference using the traditional complement, increment,
+ * and add rule, with the increment part being handled by starting the
+ * carry off at 1. If you don't think integer arithmetic is done in
+ * two's complement, too bad.
+ */
+ int nb = ip_addrsize(ip);
+ int byte = 0;
+ unsigned char *pip = ip_addr(ip);
+ unsigned char *pip2 = ip_addr(ip2);
+ int carry = 1;
+
+ while (--nb >= 0)
+ {
+ int lobyte;
+
+ carry = pip[nb] + (~pip2[nb] & 0xFF) + carry;
+ lobyte = carry & 0xFF;
+ if (byte < sizeof(int64))
+ {
+ res |= ((int64) lobyte) << (byte * 8);
+ }
+ else
+ {
+ /*
+ * Input wider than int64: check for overflow. All bytes to
+ * the left of what will fit should be 0 or 0xFF, depending on
+ * sign of the now-complete result.
+ */
+ if ((res < 0) ? (lobyte != 0xFF) : (lobyte != 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("result is out of range")));
+ }
+ carry >>= 8;
+ byte++;
+ }
+
+ /*
+ * If input is narrower than int64, overflow is not possible, but we
+ * have to do proper sign extension.
+ */
+ if (carry == 0 && byte < sizeof(int64))
+ res |= ((uint64) (int64) -1) << (byte * 8);
+ }
+
+ PG_RETURN_INT64(res);
+}
+
+
+/*
+ * clean_ipv6_addr --- remove any '%zone' part from an IPv6 address string
+ *
+ * XXX This should go away someday!
+ *
+ * This is a kluge needed because we don't yet support zones in stored inet
+ * values. Since the result of getnameinfo() might include a zone spec,
+ * call this to remove it anywhere we want to feed getnameinfo's output to
+ * network_in. Beats failing entirely.
+ *
+ * An alternative approach would be to let network_in ignore %-parts for
+ * itself, but that would mean we'd silently drop zone specs in user input,
+ * which seems not such a good idea.
+ */
+void
+clean_ipv6_addr(int addr_family, char *addr)
+{
+#ifdef HAVE_IPV6
+ if (addr_family == AF_INET6)
+ {
+ char *pct = strchr(addr, '%');
+
+ if (pct)
+ *pct = '\0';
+ }
+#endif
+}