postgresql/src/backend/utils/adt/network.c

2130 lines
48 KiB
C
Raw Normal View History

1998-10-22 22:40:50 +02:00
/*
* PostgreSQL type definitions for the INET and CIDR types.
1998-10-22 22:40:50 +02:00
*
2010-09-20 22:08:53 +02:00
* src/backend/utils/adt/network.c
*
1998-10-22 22:40:50 +02:00
* Jon Postel RIP 16 Oct 1998
*/
#include "postgres.h"
#include <sys/socket.h>
1998-10-22 22:40:50 +02:00
#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"
*) inet_(client|server)_(addr|port)() and necessary documentation for the four functions. > Also, please justify the temp-related changes. I was not aware that we > had any breakage there. patch-tmp-schema.txt contains the following bits: *) Changes pg_namespace_aclmask() so that the superuser is always able to create objects in the temp namespace. *) Changes pg_namespace_aclmask() so that if this is a temp namespace, objects are only allowed to be created in the temp namespace if the user has TEMP privs on the database. This encompasses all object creation, not just TEMP tables. *) InitTempTableNamespace() checks to see if the current user, not the session user, has access to create a temp namespace. The first two changes are necessary to support the third change. Now it's possible to revoke all temp table privs from non-super users and limiting all creation of temp tables/schemas via a function that's executed with elevated privs (security definer). Before this change, it was not possible to have a setuid function to create a temp table/schema if the session user had no TEMP privs. patch-area-path.txt contains: *) Can now determine the area of a closed path. patch-dfmgr.txt contains: *) Small tweak to add the library path that's being expanded. I was using $lib/foo.so and couldn't easily figure out what the error message, "invalid macro name in dynamic library path" meant without looking through the source code. With the path in there, at least I know where to start looking in my config file. Sean Chittenden
2004-05-26 20:35:51 +02:00
#include "libpq/libpq-be.h"
2003-05-13 20:03:08 +02:00
#include "libpq/pqformat.h"
*) inet_(client|server)_(addr|port)() and necessary documentation for the four functions. > Also, please justify the temp-related changes. I was not aware that we > had any breakage there. patch-tmp-schema.txt contains the following bits: *) Changes pg_namespace_aclmask() so that the superuser is always able to create objects in the temp namespace. *) Changes pg_namespace_aclmask() so that if this is a temp namespace, objects are only allowed to be created in the temp namespace if the user has TEMP privs on the database. This encompasses all object creation, not just TEMP tables. *) InitTempTableNamespace() checks to see if the current user, not the session user, has access to create a temp namespace. The first two changes are necessary to support the third change. Now it's possible to revoke all temp table privs from non-super users and limiting all creation of temp tables/schemas via a function that's executed with elevated privs (security definer). Before this change, it was not possible to have a setuid function to create a temp table/schema if the session user had no TEMP privs. patch-area-path.txt contains: *) Can now determine the area of a closed path. patch-dfmgr.txt contains: *) Small tweak to add the library path that's being expanded. I was using $lib/foo.so and couldn't easily figure out what the error message, "invalid macro name in dynamic library path" meant without looking through the source code. With the path in there, at least I know where to start looking in my config file. Sean Chittenden
2004-05-26 20:35:51 +02:00
#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 int network_cmp_abbrev(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);
1998-10-22 22:40:50 +02:00
/*
* Common INET/CIDR input routine
*/
1998-10-22 22:40:50 +02:00
static inet *
network_in(char *src, bool is_cidr)
1998-10-22 22:40:50 +02:00
{
2003-08-04 02:43:34 +02:00
int bits;
1998-10-22 22:40:50 +02:00
inet *dst;
dst = (inet *) palloc0(sizeof(inet));
1999-03-22 06:00:57 +01:00
/*
* First, check to see if this is an IPv6 or IPv4 address. IPv6 addresses
2005-10-15 04:49:52 +02:00
* will have a : somewhere in them (several, in fact) so if there is one
* present, assume it's V6, otherwise assume it's V4.
*/
2003-08-02 01:22:52 +02:00
if (strchr(src, ':') != NULL)
ip_family(dst) = PGSQL_AF_INET6;
2003-08-02 01:22:52 +02:00
else
ip_family(dst) = PGSQL_AF_INET;
2003-08-04 02:43:34 +02:00
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),
2003-08-04 02:43:34 +02:00
/* translator: first %s is inet or cidr */
errmsg("invalid input syntax for type %s: \"%s\"",
is_cidr ? "cidr" : "inet", src)));
/*
2005-10-15 04:49:52 +02:00
* 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.")));
}
1999-03-22 06:00:57 +01:00
1998-10-22 22:40:50 +02:00
ip_bits(dst) = bits;
SET_INET_VARSIZE(dst);
1998-10-22 22:40:50 +02:00
return dst;
}
Datum
inet_in(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
char *src = PG_GETARG_CSTRING(0);
PG_RETURN_INET_P(network_in(src, false));
1998-10-22 22:40:50 +02:00
}
Datum
cidr_in(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
char *src = PG_GETARG_CSTRING(0);
PG_RETURN_INET_P(network_in(src, true));
1998-10-22 22:40:50 +02:00
}
*) inet_(client|server)_(addr|port)() and necessary documentation for the four functions. > Also, please justify the temp-related changes. I was not aware that we > had any breakage there. patch-tmp-schema.txt contains the following bits: *) Changes pg_namespace_aclmask() so that the superuser is always able to create objects in the temp namespace. *) Changes pg_namespace_aclmask() so that if this is a temp namespace, objects are only allowed to be created in the temp namespace if the user has TEMP privs on the database. This encompasses all object creation, not just TEMP tables. *) InitTempTableNamespace() checks to see if the current user, not the session user, has access to create a temp namespace. The first two changes are necessary to support the third change. Now it's possible to revoke all temp table privs from non-super users and limiting all creation of temp tables/schemas via a function that's executed with elevated privs (security definer). Before this change, it was not possible to have a setuid function to create a temp table/schema if the session user had no TEMP privs. patch-area-path.txt contains: *) Can now determine the area of a closed path. patch-dfmgr.txt contains: *) Small tweak to add the library path that's being expanded. I was using $lib/foo.so and couldn't easily figure out what the error message, "invalid macro name in dynamic library path" meant without looking through the source code. With the path in there, at least I know where to start looking in my config file. Sean Chittenden
2004-05-26 20:35:51 +02:00
1998-10-22 22:40:50 +02:00
/*
* Common INET/CIDR output routine
1998-10-22 22:40:50 +02:00
*/
static char *
network_out(inet *src, bool is_cidr)
1998-10-22 22:40:50 +02:00
{
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
char *dst;
int len;
1998-10-22 22:40:50 +02:00
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)
1998-10-22 22:40:50 +02:00
{
len = strlen(tmp);
snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
1998-10-22 22:40:50 +02:00
}
1999-03-22 06:00:57 +01:00
return pstrdup(tmp);
1998-10-22 22:40:50 +02:00
}
Datum
inet_out(PG_FUNCTION_ARGS)
{
inet *src = PG_GETARG_INET_PP(0);
PG_RETURN_CSTRING(network_out(src, false));
}
1998-10-22 22:40:50 +02:00
Datum
cidr_out(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *src = PG_GETARG_INET_PP(0);
PG_RETURN_CSTRING(network_out(src, true));
1998-10-22 22:40:50 +02:00
}
2003-05-13 20:03:08 +02:00
/*
* network_recv - converts external binary format to inet
2003-05-13 20:03:08 +02:00
*
* 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.
2003-05-13 20:03:08 +02:00
*/
static inet *
network_recv(StringInfo buf, bool is_cidr)
2003-05-13 20:03:08 +02:00
{
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));
2003-05-13 20:03:08 +02:00
ip_family(addr) = pq_getmsgbyte(buf);
2003-08-02 01:22:52 +02:00
if (ip_family(addr) != PGSQL_AF_INET &&
ip_family(addr) != PGSQL_AF_INET6)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
2006-10-04 02:30:14 +02:00
/* translator: %s is inet or cidr */
errmsg("invalid address family in external \"%s\" value",
is_cidr ? "cidr" : "inet")));
2003-05-13 20:03:08 +02:00
bits = pq_getmsgbyte(buf);
if (bits < 0 || bits > ip_maxbits(addr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
2006-10-04 02:30:14 +02:00
/* translator: %s is inet or cidr */
errmsg("invalid bits in external \"%s\" value",
is_cidr ? "cidr" : "inet")));
2003-05-13 20:03:08 +02:00
ip_bits(addr) = bits;
i = pq_getmsgbyte(buf); /* ignore is_cidr */
2003-05-13 20:03:08 +02:00
nb = pq_getmsgbyte(buf);
if (nb != ip_addrsize(addr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
2006-10-04 02:30:14 +02:00
/* translator: %s is inet or cidr */
errmsg("invalid length in external \"%s\" value",
is_cidr ? "cidr" : "inet")));
2003-05-13 20:03:08 +02:00
2003-08-04 02:43:34 +02:00
addrptr = (char *) ip_addr(addr);
2003-05-13 20:03:08 +02:00
for (i = 0; i < nb; i++)
addrptr[i] = pq_getmsgbyte(buf);
/*
2005-10-15 04:49:52 +02:00
* Error check: CIDR values must not have any bits set beyond the masklen.
2003-05-13 20:03:08 +02:00
*/
if (is_cidr)
2003-05-13 20:03:08 +02:00
{
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.")));
2003-05-13 20:03:08 +02:00
}
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));
2003-05-13 20:03:08 +02:00
}
Datum
cidr_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
PG_RETURN_INET_P(network_recv(buf, true));
2003-05-13 20:03:08 +02:00
}
2003-05-13 20:03:08 +02:00
/*
* network_send - converts inet to binary format
2003-05-13 20:03:08 +02:00
*/
static bytea *
network_send(inet *addr, bool is_cidr)
2003-05-13 20:03:08 +02:00
{
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);
2003-05-13 20:03:08 +02:00
nb = ip_addrsize(addr);
if (nb < 0)
nb = 0;
pq_sendbyte(&buf, nb);
2003-08-04 02:43:34 +02:00
addrptr = (char *) ip_addr(addr);
2003-05-13 20:03:08 +02:00
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));
2003-05-13 20:03:08 +02:00
}
Datum
cidr_send(PG_FUNCTION_ARGS)
{
inet *addr = PG_GETARG_INET_PP(0);
PG_RETURN_BYTEA_P(network_send(addr, true));
2003-05-13 20:03:08 +02:00
}
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;
2003-08-04 02:43:34 +02:00
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;
}
1998-10-22 22:40:50 +02:00
/*
* 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.
1998-10-22 22:40:50 +02:00
*/
static int32
network_cmp_internal(inet *a1, inet *a2)
1998-10-22 22:40:50 +02:00
{
if (ip_family(a1) == ip_family(a2))
{
2001-03-22 05:01:46 +01:00
int order;
order = bitncmp(ip_addr(a1), ip_addr(a2),
2003-08-04 02:43:34 +02:00
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);
1998-10-22 22:40:50 +02:00
}
Datum
network_cmp(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_PP(0);
inet *a2 = PG_GETARG_INET_PP(1);
PG_RETURN_INT32(network_cmp_internal(a1, a2));
1998-10-22 22:40:50 +02:00
}
/*
* 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 = network_cmp_abbrev;
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);
}
/*
* Abbreviated key comparison func
*/
static int
network_cmp_abbrev(Datum x, Datum y, SortSupport ssup)
{
if (x > y)
return 1;
else if (x == y)
return 0;
else
return -1;
}
/*
* 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)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_PP(0);
inet *a2 = PG_GETARG_INET_PP(1);
PG_RETURN_BOOL(network_cmp_internal(a1, a2) < 0);
1998-10-22 22:40:50 +02:00
}
Datum
network_le(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_PP(0);
inet *a2 = PG_GETARG_INET_PP(1);
PG_RETURN_BOOL(network_cmp_internal(a1, a2) <= 0);
1998-10-22 22:40:50 +02:00
}
Datum
network_eq(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
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);
1998-10-22 22:40:50 +02:00
}
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)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_PP(0);
inet *a2 = PG_GETARG_INET_PP(1);
1999-05-25 18:15:34 +02:00
if (ip_family(a1) == ip_family(a2))
1998-10-22 22:40:50 +02:00
{
PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2) &&
bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
1998-10-22 22:40:50 +02:00
}
PG_RETURN_BOOL(false);
1998-10-22 22:40:50 +02:00
}
Datum
network_subeq(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_PP(0);
inet *a2 = PG_GETARG_INET_PP(1);
1999-03-22 06:00:57 +01:00
if (ip_family(a1) == ip_family(a2))
1998-10-22 22:40:50 +02:00
{
PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2) &&
bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
1998-10-22 22:40:50 +02:00
}
PG_RETURN_BOOL(false);
1998-10-22 22:40:50 +02:00
}
Datum
network_sup(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_PP(0);
inet *a2 = PG_GETARG_INET_PP(1);
1999-03-22 06:00:57 +01:00
if (ip_family(a1) == ip_family(a2))
1998-10-22 22:40:50 +02:00
{
PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2) &&
bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
1998-10-22 22:40:50 +02:00
}
PG_RETURN_BOOL(false);
1998-10-22 22:40:50 +02:00
}
Datum
network_supeq(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *a1 = PG_GETARG_INET_PP(0);
inet *a2 = PG_GETARG_INET_PP(1);
1999-03-22 06:00:57 +01:00
if (ip_family(a1) == ip_family(a2))
1998-10-22 22:40:50 +02:00
{
PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2) &&
bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
1998-10-22 22:40:50 +02:00
}
PG_RETURN_BOOL(false);
1998-10-22 22:40:50 +02:00
}
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)
1998-10-22 22:40:50 +02:00
{
inet *ip = PG_GETARG_INET_PP(0);
char *ptr;
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
1998-10-22 22:40:50 +02:00
/* 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")));
1999-03-22 06:00:57 +01:00
/* Suppress /n if present (shouldn't happen now) */
1998-10-22 22:40:50 +02:00
if ((ptr = strchr(tmp, '/')) != NULL)
*ptr = '\0';
PG_RETURN_TEXT_P(cstring_to_text(tmp));
1998-10-22 22:40:50 +02:00
}
/*
* 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)
1998-10-22 22:40:50 +02:00
{
inet *ip = PG_GETARG_INET_PP(0);
1999-03-22 06:00:57 +01:00
PG_RETURN_INT32(ip_bits(ip));
1998-10-22 22:40:50 +02:00
}
Datum
network_family(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_PP(0);
2003-08-04 02:43:34 +02:00
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)
1998-10-22 22:40:50 +02:00
{
inet *ip = PG_GETARG_INET_PP(0);
inet *dst;
2011-04-10 17:42:00 +02:00
int byte;
2003-08-04 02:43:34 +02:00
int bits;
int maxbytes;
unsigned char mask;
2003-08-04 02:43:34 +02:00
unsigned char *a,
*b;
/* make sure any unused bits are zeroed */
dst = (inet *) palloc0(sizeof(inet));
1998-10-22 22:40:50 +02:00
maxbytes = ip_addrsize(ip);
bits = ip_bits(ip);
a = ip_addr(ip);
b = ip_addr(dst);
2011-04-10 17:42:00 +02:00
for (byte = 0; byte < maxbytes; byte++)
2003-08-04 02:43:34 +02:00
{
if (bits >= 8)
{
mask = 0x00;
bits -= 8;
2003-08-04 02:43:34 +02:00
}
else if (bits == 0)
mask = 0xff;
2003-08-04 02:43:34 +02:00
else
{
mask = 0xff >> bits;
bits = 0;
}
2011-04-10 17:42:00 +02:00
b[byte] = a[byte] | mask;
2003-08-04 02:43:34 +02:00
}
1999-03-22 06:00:57 +01:00
ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_bits(ip);
SET_INET_VARSIZE(dst);
PG_RETURN_INET_P(dst);
1998-10-22 22:40:50 +02:00
}
Datum
network_network(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *ip = PG_GETARG_INET_PP(0);
inet *dst;
2011-04-10 17:42:00 +02:00
int byte;
2003-08-04 02:43:34 +02:00
int bits;
unsigned char mask;
2003-08-04 02:43:34 +02:00
unsigned char *a,
*b;
/* make sure any unused bits are zeroed */
dst = (inet *) palloc0(sizeof(inet));
1998-10-22 22:40:50 +02:00
bits = ip_bits(ip);
a = ip_addr(ip);
b = ip_addr(dst);
byte = 0;
2003-08-04 02:43:34 +02:00
while (bits)
{
if (bits >= 8)
{
mask = 0xff;
bits -= 8;
2003-08-04 02:43:34 +02:00
}
else
{
mask = 0xff << (8 - bits);
bits = 0;
}
2011-04-10 17:42:00 +02:00
b[byte] = a[byte] & mask;
byte++;
2003-08-04 02:43:34 +02:00
}
1999-03-22 06:00:57 +01:00
ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_bits(ip);
SET_INET_VARSIZE(dst);
PG_RETURN_INET_P(dst);
1998-10-22 22:40:50 +02:00
}
Datum
network_netmask(PG_FUNCTION_ARGS)
1998-10-22 22:40:50 +02:00
{
inet *ip = PG_GETARG_INET_PP(0);
inet *dst;
2011-04-10 17:42:00 +02:00
int byte;
2003-08-04 02:43:34 +02:00
int bits;
unsigned char mask;
unsigned char *b;
/* make sure any unused bits are zeroed */
dst = (inet *) palloc0(sizeof(inet));
1998-10-22 22:40:50 +02:00
bits = ip_bits(ip);
b = ip_addr(dst);
byte = 0;
2003-08-04 02:43:34 +02:00
while (bits)
{
if (bits >= 8)
{
mask = 0xff;
bits -= 8;
2003-08-04 02:43:34 +02:00
}
else
{
mask = 0xff << (8 - bits);
bits = 0;
}
b[byte] = mask;
2011-04-10 17:42:00 +02:00
byte++;
2003-08-04 02:43:34 +02:00
}
1999-03-22 06:00:57 +01:00
ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_maxbits(ip);
SET_INET_VARSIZE(dst);
PG_RETURN_INET_P(dst);
1998-10-22 22:40:50 +02:00
}
Datum
network_hostmask(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_PP(0);
inet *dst;
2011-04-10 17:42:00 +02:00
int byte;
2003-08-04 02:43:34 +02:00
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;
2003-08-04 02:43:34 +02:00
while (bits)
{
if (bits >= 8)
{
mask = 0xff;
bits -= 8;
2003-08-04 02:43:34 +02:00
}
else
{
mask = 0xff >> (8 - bits);
bits = 0;
}
b[byte] = mask;
2011-04-10 17:42:00 +02:00
byte--;
2003-08-04 02:43:34 +02:00
}
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.
Fix assorted issues in convert_to_scalar(). If convert_to_scalar is passed a pair of datatypes it can't cope with, its former behavior was just to elog(ERROR). While this is OK so far as the core code is concerned, there's extension code that would like to use scalarltsel/scalargtsel/etc as selectivity estimators for operators that work on non-core datatypes, and this behavior is a show-stopper for that use-case. If we simply allow convert_to_scalar to return FALSE instead of outright failing, then the main logic of scalarltsel/scalargtsel will work fine for any operator that behaves like a scalar inequality comparison. The lack of conversion capability will mean that we can't estimate to better than histogram-bin-width precision, since the code will effectively assume that the comparison constant falls at the middle of its bin. But that's still a lot better than nothing. (Someday we should provide a way for extension code to supply a custom version of convert_to_scalar, but today is not that day.) While poking at this issue, we noted that the existing code for handling type bytea in convert_to_scalar is several bricks shy of a load. It assumes without checking that if the comparison value is type bytea, the bounds values are too; in the worst case this could lead to a crash. It also fails to detoast the input values, so that the comparison result is complete garbage if any input is toasted out-of-line, compressed, or even just short-header. I'm not sure how often such cases actually occur --- the bounds values, at least, are probably safe since they are elements of an array and hence can't be toasted. But that doesn't make this code OK. Back-patch to all supported branches, partly because author requested that, but mostly because of the bytea bugs. The change in API for the exposed routine convert_network_to_scalar() is theoretically a back-patch hazard, but it seems pretty unlikely that any third-party code is calling that function directly. Tomas Vondra, with some adjustments by me Discussion: https://postgr.es/m/b68441b6-d18f-13ab-b43b-9a72188a4e02@2ndquadrant.com
2018-03-04 02:31:35 +01:00
*
* On failure (e.g., unsupported typid), set *failure to true;
* otherwise, that variable is not changed.
*/
double
Fix assorted issues in convert_to_scalar(). If convert_to_scalar is passed a pair of datatypes it can't cope with, its former behavior was just to elog(ERROR). While this is OK so far as the core code is concerned, there's extension code that would like to use scalarltsel/scalargtsel/etc as selectivity estimators for operators that work on non-core datatypes, and this behavior is a show-stopper for that use-case. If we simply allow convert_to_scalar to return FALSE instead of outright failing, then the main logic of scalarltsel/scalargtsel will work fine for any operator that behaves like a scalar inequality comparison. The lack of conversion capability will mean that we can't estimate to better than histogram-bin-width precision, since the code will effectively assume that the comparison constant falls at the middle of its bin. But that's still a lot better than nothing. (Someday we should provide a way for extension code to supply a custom version of convert_to_scalar, but today is not that day.) While poking at this issue, we noted that the existing code for handling type bytea in convert_to_scalar is several bricks shy of a load. It assumes without checking that if the comparison value is type bytea, the bounds values are too; in the worst case this could lead to a crash. It also fails to detoast the input values, so that the comparison result is complete garbage if any input is toasted out-of-line, compressed, or even just short-header. I'm not sure how often such cases actually occur --- the bounds values, at least, are probably safe since they are elements of an array and hence can't be toasted. But that doesn't make this code OK. Back-patch to all supported branches, partly because author requested that, but mostly because of the bytea bugs. The change in API for the exposed routine convert_network_to_scalar() is theoretically a back-patch hazard, but it seems pretty unlikely that any third-party code is calling that function directly. Tomas Vondra, with some adjustments by me Discussion: https://postgr.es/m/b68441b6-d18f-13ab-b43b-9a72188a4e02@2ndquadrant.com
2018-03-04 02:31:35 +01:00
convert_network_to_scalar(Datum value, Oid typid, bool *failure)
{
switch (typid)
{
case INETOID:
case CIDROID:
{
inet *ip = DatumGetInetPP(value);
2003-08-04 02:43:34 +02:00
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);
2003-08-04 02:43:34 +02:00
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;
}
}
Fix assorted issues in convert_to_scalar(). If convert_to_scalar is passed a pair of datatypes it can't cope with, its former behavior was just to elog(ERROR). While this is OK so far as the core code is concerned, there's extension code that would like to use scalarltsel/scalargtsel/etc as selectivity estimators for operators that work on non-core datatypes, and this behavior is a show-stopper for that use-case. If we simply allow convert_to_scalar to return FALSE instead of outright failing, then the main logic of scalarltsel/scalargtsel will work fine for any operator that behaves like a scalar inequality comparison. The lack of conversion capability will mean that we can't estimate to better than histogram-bin-width precision, since the code will effectively assume that the comparison constant falls at the middle of its bin. But that's still a lot better than nothing. (Someday we should provide a way for extension code to supply a custom version of convert_to_scalar, but today is not that day.) While poking at this issue, we noted that the existing code for handling type bytea in convert_to_scalar is several bricks shy of a load. It assumes without checking that if the comparison value is type bytea, the bounds values are too; in the worst case this could lead to a crash. It also fails to detoast the input values, so that the comparison result is complete garbage if any input is toasted out-of-line, compressed, or even just short-header. I'm not sure how often such cases actually occur --- the bounds values, at least, are probably safe since they are elements of an array and hence can't be toasted. But that doesn't make this code OK. Back-patch to all supported branches, partly because author requested that, but mostly because of the bytea bugs. The change in API for the exposed routine convert_network_to_scalar() is theoretically a back-patch hazard, but it seems pretty unlikely that any third-party code is calling that function directly. Tomas Vondra, with some adjustments by me Discussion: https://postgr.es/m/b68441b6-d18f-13ab-b43b-9a72188a4e02@2ndquadrant.com
2018-03-04 02:31:35 +01:00
*failure = true;
return 0;
}
1998-10-22 22:40:50 +02:00
/*
* int
* bitncmp(l, r, n)
2003-08-04 02:43:34 +02:00
* compare bit masks l and r, for n bits.
* return:
* <0, >0, or 0 in the libc tradition.
* note:
2003-08-04 02:43:34 +02:00
* network byte order assumed. this means 192.5.5.240/28 has
* 0x11110000 in its fourth octet.
* author:
2003-08-04 02:43:34 +02:00
* Paul Vixie (ISC), June 1996
1998-10-22 22:40:50 +02:00
*/
int
bitncmp(const unsigned char *l, const unsigned char *r, int n)
{
unsigned int lb,
2003-08-04 02:43:34 +02:00
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];
2003-08-04 02:43:34 +02:00
for (b = n % 8; b > 0; b--)
{
if (IS_HIGHBIT_SET(lb) != IS_HIGHBIT_SET(rb))
2003-08-04 02:43:34 +02:00
{
if (IS_HIGHBIT_SET(lb))
return 1;
return -1;
}
lb <<= 1;
rb <<= 1;
}
return 0;
1998-10-22 22:40:50 +02:00
}
/*
* 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)
{
2011-04-10 17:42:00 +02:00
int byte;
2003-08-04 02:43:34 +02:00
int nbits;
int maxbits;
int maxbytes;
unsigned char mask;
2003-08-04 02:43:34 +02:00
if (family == PGSQL_AF_INET)
{
maxbits = 32;
maxbytes = 4;
2003-08-04 02:43:34 +02:00
}
else
{
maxbits = 128;
maxbytes = 16;
}
2003-08-02 01:22:52 +02:00
Assert(bits <= maxbits);
if (bits == maxbits)
2003-08-02 01:22:52 +02:00
return true;
byte = bits / 8;
nbits = bits % 8;
mask = 0xff;
if (bits != 0)
mask >>= nbits;
2011-04-10 17:42:00 +02:00
while (byte < maxbytes)
2003-08-04 02:43:34 +02:00
{
2011-04-10 17:42:00 +02:00
if ((a[byte] & mask) != 0)
2003-08-02 01:22:52 +02:00
return false;
mask = 0xff;
2011-04-10 17:42:00 +02:00
byte++;
}
2003-08-02 01:22:52 +02:00
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),
2003-08-04 02:43:34 +02:00
Int32GetDatum(-1));
}
/*
* IP address that the client is connecting from (NULL if Unix socket)
*/
Datum
inet_client_addr(PG_FUNCTION_ARGS)
{
2004-08-29 07:07:03 +02:00
Port *port = MyProcPort;
char remote_host[NI_MAXHOST];
int ret;
if (port == NULL)
PG_RETURN_NULL();
2004-08-29 07:07:03 +02:00
switch (port->raddr.addr.ss_family)
{
case AF_INET:
#ifdef HAVE_IPV6
2004-08-29 07:07:03 +02:00
case AF_INET6:
#endif
2004-08-29 07:07:03 +02:00
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)
{
2004-08-29 07:07:03 +02:00
Port *port = MyProcPort;
char remote_port[NI_MAXSERV];
int ret;
if (port == NULL)
PG_RETURN_NULL();
2004-08-29 07:07:03 +02:00
switch (port->raddr.addr.ss_family)
{
case AF_INET:
#ifdef HAVE_IPV6
2004-08-29 07:07:03 +02:00
case AF_INET6:
#endif
2004-08-29 07:07:03 +02:00
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)
{
2004-08-29 07:07:03 +02:00
Port *port = MyProcPort;
char local_host[NI_MAXHOST];
int ret;
if (port == NULL)
PG_RETURN_NULL();
2004-08-29 07:07:03 +02:00
switch (port->laddr.addr.ss_family)
{
case AF_INET:
#ifdef HAVE_IPV6
2004-08-29 07:07:03 +02:00
case AF_INET6:
#endif
2004-08-29 07:07:03 +02:00
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)
{
2004-08-29 07:07:03 +02:00
Port *port = MyProcPort;
char local_port[NI_MAXSERV];
int ret;
if (port == NULL)
PG_RETURN_NULL();
2004-08-29 07:07:03 +02:00
switch (port->laddr.addr.ss_family)
{
case AF_INET:
#ifdef HAVE_IPV6
2004-08-29 07:07:03 +02:00
case AF_INET6:
#endif
2004-08-29 07:07:03 +02:00
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));
{
2006-10-04 02:30:14 +02:00
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
{
2006-10-04 02:30:14 +02:00
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
{
2006-10-04 02:30:14 +02:00
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));
{
2006-10-04 02:30:14 +02:00
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;
2006-10-04 02:30:14 +02:00
/*
* We have to be careful about right-shifting addend because
2006-10-04 02:30:14 +02:00
* 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;
}
2006-10-04 02:30:14 +02:00
/*
2006-10-04 02:30:14 +02:00
* 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);
2006-10-04 02:30:14 +02:00
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);
2006-10-04 02:30:14 +02:00
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
{
/*
2006-10-04 02:30:14 +02:00
* 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
2006-10-04 02:30:14 +02:00
* two's complement, too bad.
*/
2006-10-04 02:30:14 +02:00
int nb = ip_addrsize(ip);
2011-04-10 17:42:00 +02:00
int byte = 0;
2006-10-04 02:30:14 +02:00
unsigned char *pip = ip_addr(ip);
unsigned char *pip2 = ip_addr(ip2);
int carry = 1;
while (nb-- > 0)
{
2006-10-04 02:30:14 +02:00
int lobyte;
carry = pip[nb] + (~pip2[nb] & 0xFF) + carry;
lobyte = carry & 0xFF;
2011-04-10 17:42:00 +02:00
if (byte < sizeof(int64))
{
2011-04-10 17:42:00 +02:00
res |= ((int64) lobyte) << (byte * 8);
}
else
{
/*
* Input wider than int64: check for overflow. All bytes to
2006-10-04 02:30:14 +02:00
* 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;
2011-04-10 17:42:00 +02:00
byte++;
}
/*
2006-10-04 02:30:14 +02:00
* If input is narrower than int64, overflow is not possible, but we
* have to do proper sign extension.
*/
2011-04-10 17:42:00 +02:00
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)
{
2007-11-15 22:14:46 +01:00
char *pct = strchr(addr, '%');
if (pct)
*pct = '\0';
}
#endif
}