/*
* 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, Node *escontext)
{
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)))
ereturn(escontext, NULL,
(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)))
ereturn(escontext, NULL,
(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 , fcinfo->context));
}
Datum
cidr_in(PG_FUNCTION_ARGS)
{
char *src = PG_GETARG_CSTRING(0 );
PG_RETURN_INET_P(network_in(src, true , fcinfo->context));
}
/*
* 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);
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 ] &= ~(0 xFF >> (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 > 100 k 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 )
{
if (trace_sort)
elog(LOG,
"network_abbrev: estimation ends at cardinality %f"
" after " INT64_FORMAT " values (%d rows)" ,
abbr_card, uss->input_count, memtupcount);
uss->estimating = false ;
return false ;
}
/*
* Target minimum cardinality is 1 per ~ 2 k 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 2 k
* ( non - null ) rows .
*/
if (abbr_card < uss->input_count / 2000 .0 + 0 .5 )
{
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);
return true ;
}
if (trace_sort)
elog(LOG,
"network_abbrev: cardinality %f after " INT64_FORMAT
" values (%d rows)" , abbr_card, uss->input_count, memtupcount);
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;
/*
* create clause " key > = network_scan_first ( rightopval ) " , or " > " if the
* operator disallows equality .
*/
opr1oid = get_opfamily_member_for_cmptype(opfamily, datatype, datatype, is_eq ? COMPARE_GE : COMPARE_GT);
if (opr1oid == InvalidOid)
return NIL;
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_for_cmptype(opfamily, datatype, datatype, COMPARE_LE);
if (opr2oid == InvalidOid)
return NIL;
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 = 0 x00;
bits -= 8 ;
}
else if (bits == 0 )
mask = 0 xff;
else
{
mask = 0 xff >> 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 = 0 xff;
bits -= 8 ;
}
else
{
mask = 0 xff << (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 = 0 xff;
bits -= 8 ;
}
else
{
mask = 0 xff << (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 = 0 xff;
bits -= 8 ;
}
else
{
mask = 0 xff >> (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
* 0 x11110000 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 = 0 xff;
if (bits != 0 )
mask >>= nbits;
while (byte < maxbytes)
{
if ((a[byte] & mask) != 0 )
return false ;
mask = 0 xff;
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:
case AF_INET6:
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 , NULL));
}
/*
* 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:
case AF_INET6:
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:
case AF_INET6:
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 , NULL));
}
/*
* 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:
case AF_INET6:
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 & 0 xFF) + carry;
pdst[nb] = (unsigned char ) (carry & 0 xFF);
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) 0 xFF);
addend /= 0 x100;
}
/*
* 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] & 0 xFF) + carry;
lobyte = carry & 0 xFF;
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 0 xFF , depending on
* sign of the now - complete result .
*/
if ((res < 0 ) ? (lobyte != 0 xFF) : (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)
{
if (addr_family == AF_INET6)
{
char *pct = strchr(addr, '%' );
if (pct)
*pct = '\0' ;
}
}
Messung V0.5 in Prozent C=95 H=95 G=94
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.65Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
*Eine klare Vorstellung vom Zielzustand