1998-10-22 22:40:50 +02:00
|
|
|
/*
|
2003-06-25 00:21:24 +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
|
2000-07-06 07:48:31 +02:00
|
|
|
*
|
1998-10-22 22:40:50 +02:00
|
|
|
* Jon Postel RIP 16 Oct 1998
|
|
|
|
*/
|
|
|
|
|
2000-07-06 07:48:31 +02:00
|
|
|
#include "postgres.h"
|
2000-02-21 19:49:54 +01:00
|
|
|
|
2000-07-06 07:48:31 +02:00
|
|
|
#include <sys/socket.h>
|
1998-10-22 22:40:50 +02:00
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
2004-06-13 23:57:28 +02:00
|
|
|
#include "access/hash.h"
|
2019-02-12 03:26:08 +01:00
|
|
|
#include "catalog/pg_opfamily.h"
|
2001-06-10 00:16:18 +02:00
|
|
|
#include "catalog/pg_type.h"
|
2016-09-02 12:49:59 +02:00
|
|
|
#include "common/ip.h"
|
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"
|
2004-05-26 20:35:51 +02:00
|
|
|
#include "miscadmin.h"
|
2019-02-12 03:26:08 +01:00
|
|
|
#include "nodes/makefuncs.h"
|
|
|
|
#include "nodes/nodeFuncs.h"
|
|
|
|
#include "nodes/supportnodes.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "utils/builtins.h"
|
2019-02-12 03:26:08 +01:00
|
|
|
#include "utils/fmgroids.h"
|
2000-08-04 01:07:51 +02:00
|
|
|
#include "utils/inet.h"
|
2019-02-12 03:26:08 +01:00
|
|
|
#include "utils/lsyscache.h"
|
2000-07-06 07:48:31 +02:00
|
|
|
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
static int32 network_cmp_internal(inet *a1, inet *a2);
|
2019-02-12 03:26:08 +01:00
|
|
|
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);
|
2003-06-25 00:21:24 +02:00
|
|
|
static bool addressOK(unsigned char *a, int bits, int family);
|
2006-02-11 21:39:59 +01:00
|
|
|
static inet *internal_inetpl(inet *ip, int64 addend);
|
1998-10-22 22:40:50 +02:00
|
|
|
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
/*
|
|
|
|
* Common INET/CIDR input routine
|
|
|
|
*/
|
1998-10-22 22:40:50 +02:00
|
|
|
static inet *
|
2006-01-23 22:45:47 +01:00
|
|
|
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;
|
|
|
|
|
2007-04-06 06:21:44 +02:00
|
|
|
dst = (inet *) palloc0(sizeof(inet));
|
1999-03-22 06:00:57 +01:00
|
|
|
|
2000-10-27 03:55:23 +02:00
|
|
|
/*
|
2014-05-06 18:12:18 +02: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.
|
2000-10-27 03:55:23 +02:00
|
|
|
*/
|
2003-06-25 00:21:24 +02:00
|
|
|
|
2003-08-02 01:22:52 +02:00
|
|
|
if (strchr(src, ':') != NULL)
|
2003-06-25 00:21:24 +02:00
|
|
|
ip_family(dst) = PGSQL_AF_INET6;
|
2003-08-02 01:22:52 +02:00
|
|
|
else
|
2003-06-25 00:21:24 +02:00
|
|
|
ip_family(dst) = PGSQL_AF_INET;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
bits = inet_net_pton(ip_family(dst), src, ip_addr(dst),
|
2006-01-23 22:45:47 +01:00
|
|
|
is_cidr ? ip_addrsize(dst) : -1);
|
2003-06-25 00:21:24 +02:00
|
|
|
if ((bits < 0) || (bits > ip_maxbits(dst)))
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
2003-08-04 02:43:34 +02:00
|
|
|
/* translator: first %s is inet or cidr */
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("invalid input syntax for type %s: \"%s\"",
|
2006-01-23 22:45:47 +01:00
|
|
|
is_cidr ? "cidr" : "inet", src)));
|
2003-06-25 00:21:24 +02:00
|
|
|
|
2003-07-27 06:53:12 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Error check: CIDR values must not have any bits set beyond the masklen.
|
2003-07-27 06:53:12 +02:00
|
|
|
*/
|
2006-01-23 22:45:47 +01:00
|
|
|
if (is_cidr)
|
2000-10-27 03:55:23 +02:00
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
if (!addressOK(ip_addr(dst), bits, ip_family(dst)))
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
errmsg("invalid cidr value: \"%s\"", src),
|
|
|
|
errdetail("Value has bits set to right of mask.")));
|
2000-10-27 03:55:23 +02:00
|
|
|
}
|
1999-03-22 06:00:57 +01:00
|
|
|
|
1998-10-22 22:40:50 +02:00
|
|
|
ip_bits(dst) = bits;
|
2007-04-06 06:21:44 +02:00
|
|
|
SET_INET_VARSIZE(dst);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
1998-10-22 22:40:50 +02:00
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
inet_in(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2000-08-04 01:07:51 +02:00
|
|
|
char *src = PG_GETARG_CSTRING(0);
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
PG_RETURN_INET_P(network_in(src, false));
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
cidr_in(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2000-08-04 01:07:51 +02:00
|
|
|
char *src = PG_GETARG_CSTRING(0);
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
PG_RETURN_INET_P(network_in(src, true));
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2004-05-26 20:35:51 +02:00
|
|
|
|
1998-10-22 22:40:50 +02:00
|
|
|
/*
|
2006-01-26 03:35:51 +01:00
|
|
|
* Common INET/CIDR output routine
|
1998-10-22 22:40:50 +02:00
|
|
|
*/
|
2006-01-26 03:35:51 +01:00
|
|
|
static char *
|
|
|
|
network_out(inet *src, bool is_cidr)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
|
2000-08-04 01:07:51 +02:00
|
|
|
char *dst;
|
2000-12-22 19:00:24 +01:00
|
|
|
int len;
|
1998-10-22 22:40:50 +02:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
dst = inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src),
|
2003-08-04 02:43:34 +02:00
|
|
|
tmp, sizeof(tmp));
|
2003-06-25 00:21:24 +02:00
|
|
|
if (dst == NULL)
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
|
|
|
errmsg("could not format inet value: %m")));
|
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
/* For CIDR, add /n if not present */
|
2006-01-26 03:35:51 +01:00
|
|
|
if (is_cidr && strchr(tmp, '/') == NULL)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2003-06-25 00:21:24 +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
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
return pstrdup(tmp);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
Datum
|
|
|
|
inet_out(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *src = PG_GETARG_INET_PP(0);
|
2006-01-26 03:35:51 +01:00
|
|
|
|
|
|
|
PG_RETURN_CSTRING(network_out(src, false));
|
|
|
|
}
|
1998-10-22 22:40:50 +02:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
cidr_out(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *src = PG_GETARG_INET_PP(0);
|
2006-01-26 03:35:51 +01:00
|
|
|
|
|
|
|
PG_RETURN_CSTRING(network_out(src, true));
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
|
2003-05-13 20:03:08 +02:00
|
|
|
/*
|
2006-01-26 03:35:51 +01: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)
|
2006-01-23 22:45:47 +01:00
|
|
|
* family, bits, is_cidr, address length, address in network byte order.
|
2006-01-26 03:35:51 +01:00
|
|
|
*
|
|
|
|
* Presence of is_cidr is largely for historical reasons, though it might
|
2014-05-06 18:12:18 +02:00
|
|
|
* allow some code-sharing on the client side. We send it correctly on
|
2006-01-26 03:35:51 +01:00
|
|
|
* output, but ignore the value on input.
|
2003-05-13 20:03:08 +02:00
|
|
|
*/
|
2006-01-26 03:35:51 +01: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 */
|
2007-04-06 06:21:44 +02:00
|
|
|
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)
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
2006-10-04 02:30:14 +02:00
|
|
|
/* translator: %s is inet or cidr */
|
2006-01-26 03:35:51 +01:00
|
|
|
errmsg("invalid address family in external \"%s\" value",
|
|
|
|
is_cidr ? "cidr" : "inet")));
|
2003-05-13 20:03:08 +02:00
|
|
|
bits = pq_getmsgbyte(buf);
|
2003-06-25 00:21:24 +02:00
|
|
|
if (bits < 0 || bits > ip_maxbits(addr))
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
2006-10-04 02:30:14 +02:00
|
|
|
/* translator: %s is inet or cidr */
|
2006-01-26 03:35:51 +01:00
|
|
|
errmsg("invalid bits in external \"%s\" value",
|
|
|
|
is_cidr ? "cidr" : "inet")));
|
2003-05-13 20:03:08 +02:00
|
|
|
ip_bits(addr) = bits;
|
2006-01-26 03:35:51 +01:00
|
|
|
i = pq_getmsgbyte(buf); /* ignore is_cidr */
|
2003-05-13 20:03:08 +02:00
|
|
|
nb = pq_getmsgbyte(buf);
|
|
|
|
if (nb != ip_addrsize(addr))
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
2006-10-04 02:30:14 +02:00
|
|
|
/* translator: %s is inet or cidr */
|
2006-01-26 03:35:51 +01:00
|
|
|
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
|
|
|
*/
|
2006-01-26 03:35:51 +01:00
|
|
|
if (is_cidr)
|
2003-05-13 20:03:08 +02:00
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
if (!addressOK(ip_addr(addr), bits, ip_family(addr)))
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
2003-09-29 02:05:25 +02:00
|
|
|
errmsg("invalid external \"cidr\" value"),
|
2003-07-27 06:53:12 +02:00
|
|
|
errdetail("Value has bits set to right of mask.")));
|
2003-05-13 20:03:08 +02:00
|
|
|
}
|
|
|
|
|
2007-04-06 06:21:44 +02:00
|
|
|
SET_INET_VARSIZE(addr);
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
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)
|
|
|
|
{
|
2006-01-26 03:35:51 +01:00
|
|
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
|
|
|
|
|
|
|
PG_RETURN_INET_P(network_recv(buf, true));
|
2003-05-13 20:03:08 +02:00
|
|
|
}
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
|
2003-05-13 20:03:08 +02:00
|
|
|
/*
|
2006-01-26 03:35:51 +01:00
|
|
|
* network_send - converts inet to binary format
|
2003-05-13 20:03:08 +02:00
|
|
|
*/
|
2006-01-26 03:35:51 +01: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));
|
2006-01-26 03:35:51 +01:00
|
|
|
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]);
|
2006-01-26 03:35:51 +01:00
|
|
|
return pq_endtypsend(&buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
|
|
|
inet_send(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *addr = PG_GETARG_INET_PP(0);
|
2006-01-26 03:35:51 +01:00
|
|
|
|
|
|
|
PG_RETURN_BYTEA_P(network_send(addr, false));
|
2003-05-13 20:03:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
|
|
|
cidr_send(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *addr = PG_GETARG_INET_PP(0);
|
2006-01-26 03:35:51 +01:00
|
|
|
|
|
|
|
PG_RETURN_BYTEA_P(network_send(addr, true));
|
2003-05-13 20:03:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-06-13 23:09:00 +02:00
|
|
|
Datum
|
2006-01-26 03:35:51 +01:00
|
|
|
inet_to_cidr(PG_FUNCTION_ARGS)
|
2001-06-13 23:09:00 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *src = PG_GETARG_INET_PP(0);
|
2006-01-26 03:35:51 +01:00
|
|
|
int bits;
|
|
|
|
|
|
|
|
bits = ip_bits(src);
|
|
|
|
|
|
|
|
/* safety check */
|
|
|
|
if ((bits < 0) || (bits > ip_maxbits(src)))
|
|
|
|
elog(ERROR, "invalid inet bit length: %d", bits);
|
|
|
|
|
2016-08-23 15:39:54 +02:00
|
|
|
PG_RETURN_INET_P(cidr_set_masklen_internal(src, bits));
|
2001-06-13 23:09:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
|
|
|
inet_set_masklen(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *src = PG_GETARG_INET_PP(0);
|
2001-10-25 07:50:21 +02:00
|
|
|
int bits = PG_GETARG_INT32(1);
|
|
|
|
inet *dst;
|
2001-06-13 23:09:00 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
if (bits == -1)
|
|
|
|
bits = ip_maxbits(src);
|
2003-06-25 00:21:24 +02:00
|
|
|
|
|
|
|
if ((bits < 0) || (bits > ip_maxbits(src)))
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("invalid mask length: %d", bits)));
|
2001-06-13 23:09:00 +02:00
|
|
|
|
|
|
|
/* clone the original data */
|
2007-04-06 06:21:44 +02:00
|
|
|
dst = (inet *) palloc(VARSIZE_ANY(src));
|
|
|
|
memcpy(dst, src, VARSIZE_ANY(src));
|
2001-06-13 23:09:00 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
ip_bits(dst) = bits;
|
2001-06-13 23:09:00 +02:00
|
|
|
|
|
|
|
PG_RETURN_INET_P(dst);
|
|
|
|
}
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
Datum
|
|
|
|
cidr_set_masklen(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *src = PG_GETARG_INET_PP(0);
|
2006-01-26 03:35:51 +01:00
|
|
|
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)));
|
|
|
|
|
2016-08-23 15:39:54 +02:00
|
|
|
PG_RETURN_INET_P(cidr_set_masklen_internal(src, bits));
|
|
|
|
}
|
2006-01-26 03:35:51 +01:00
|
|
|
|
2016-08-23 15:39:54 +02:00
|
|
|
/*
|
|
|
|
* 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));
|
2006-01-26 03:35:51 +01:00
|
|
|
|
2016-08-23 15:39:54 +02:00
|
|
|
ip_family(dst) = ip_family(src);
|
|
|
|
ip_bits(dst) = bits;
|
2009-06-11 16:49:15 +02:00
|
|
|
|
2016-08-23 15:39:54 +02:00
|
|
|
if (bits > 0)
|
2006-01-26 03:35:51 +01:00
|
|
|
{
|
2016-08-23 15:39:54 +02:00
|
|
|
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));
|
2006-01-26 03:35:51 +01:00
|
|
|
}
|
|
|
|
|
2016-08-23 15:39:54 +02:00
|
|
|
/* Set varlena header correctly */
|
|
|
|
SET_INET_VARSIZE(dst);
|
|
|
|
|
|
|
|
return dst;
|
2006-01-26 03:35:51 +01:00
|
|
|
}
|
|
|
|
|
1998-10-22 22:40:50 +02:00
|
|
|
/*
|
2000-08-04 01:07:51 +02:00
|
|
|
* Basic comparison function for sorting and inet/cidr comparisons.
|
|
|
|
*
|
2000-10-27 03:55:23 +02:00
|
|
|
* 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
|
|
|
*/
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
static int32
|
|
|
|
network_cmp_internal(inet *a1, inet *a2)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
if (ip_family(a1) == ip_family(a2))
|
2000-02-21 19:49:54 +01:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
int order;
|
2000-02-21 19:49:54 +01:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
order = bitncmp(ip_addr(a1), ip_addr(a2),
|
2003-08-04 02:43:34 +02:00
|
|
|
Min(ip_bits(a1), ip_bits(a2)));
|
2000-10-27 03:55:23 +02:00
|
|
|
if (order != 0)
|
|
|
|
return order;
|
|
|
|
order = ((int) ip_bits(a1)) - ((int) ip_bits(a2));
|
2000-08-04 01:07:51 +02:00
|
|
|
if (order != 0)
|
|
|
|
return order;
|
2003-06-25 00:21:24 +02:00
|
|
|
return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
|
2000-02-21 19:49:54 +01:00
|
|
|
}
|
2003-06-25 00:21:24 +02:00
|
|
|
|
|
|
|
return ip_family(a1) - ip_family(a2);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
network_cmp(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_INT32(network_cmp_internal(a1, a2));
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
/*
|
|
|
|
* Boolean ordering tests.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
network_lt(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(network_cmp_internal(a1, a2) < 0);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
network_le(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(network_cmp_internal(a1, a2) <= 0);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
network_eq(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
2000-02-21 19:49:54 +01:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
PG_RETURN_BOOL(network_cmp_internal(a1, a2) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
|
|
|
network_ge(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(network_cmp_internal(a1, a2) >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
|
|
|
network_gt(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(network_cmp_internal(a1, a2) > 0);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
network_ne(PG_FUNCTION_ARGS)
|
2000-02-21 19:49:54 +01:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
2000-08-04 01:07:51 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(network_cmp_internal(a1, a2) != 0);
|
2000-02-21 19:49:54 +01:00
|
|
|
}
|
|
|
|
|
2014-08-29 04:37:58 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2004-06-13 23:57:28 +02:00
|
|
|
/*
|
|
|
|
* Support function for hash indexes on inet/cidr.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
hashinet(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *addr = PG_GETARG_INET_PP(0);
|
2004-06-13 23:57:28 +02:00
|
|
|
int addrsize = ip_addrsize(addr);
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
/* XXX this assumes there are no pad bytes in the data structure */
|
2007-04-06 06:21:44 +02:00
|
|
|
return hash_any((unsigned char *) VARDATA_ANY(addr), addrsize + 2);
|
2004-06-13 23:57:28 +02:00
|
|
|
}
|
|
|
|
|
2017-09-01 04:21:21 +02:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
/*
|
|
|
|
* Boolean network-inclusion tests.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
network_sub(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
1999-05-25 18:15:34 +02:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
if (ip_family(a1) == ip_family(a2))
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
Fix header comment for bitncmp().
The result is an int less than, equal to, or greater than zero, in the
style of memcmp (and, in fact, exactly the output of memcmp in some cases).
This comment previously said -1, 1, or 0, which was an overspecification,
as noted by Emre Hasegeli. All of the existing callers appear to be fine
with the actual behavior, so just fix the comment.
In passing, improve infelicitous formatting of some call sites.
2014-01-04 20:01:51 +01: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
|
|
|
}
|
2003-06-25 00:21:24 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(false);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
network_subeq(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
1999-03-22 06:00:57 +01:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
if (ip_family(a1) == ip_family(a2))
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
Fix header comment for bitncmp().
The result is an int less than, equal to, or greater than zero, in the
style of memcmp (and, in fact, exactly the output of memcmp in some cases).
This comment previously said -1, 1, or 0, which was an overspecification,
as noted by Emre Hasegeli. All of the existing callers appear to be fine
with the actual behavior, so just fix the comment.
In passing, improve infelicitous formatting of some call sites.
2014-01-04 20:01:51 +01: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
|
|
|
}
|
2003-06-25 00:21:24 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(false);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
network_sup(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
1999-03-22 06:00:57 +01:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
if (ip_family(a1) == ip_family(a2))
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
Fix header comment for bitncmp().
The result is an int less than, equal to, or greater than zero, in the
style of memcmp (and, in fact, exactly the output of memcmp in some cases).
This comment previously said -1, 1, or 0, which was an overspecification,
as noted by Emre Hasegeli. All of the existing callers appear to be fine
with the actual behavior, so just fix the comment.
In passing, improve infelicitous formatting of some call sites.
2014-01-04 20:01:51 +01: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
|
|
|
}
|
2003-06-25 00:21:24 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(false);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
network_supeq(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *a1 = PG_GETARG_INET_PP(0);
|
|
|
|
inet *a2 = PG_GETARG_INET_PP(1);
|
1999-03-22 06:00:57 +01:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
if (ip_family(a1) == ip_family(a2))
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
Fix header comment for bitncmp().
The result is an int less than, equal to, or greater than zero, in the
style of memcmp (and, in fact, exactly the output of memcmp in some cases).
This comment previously said -1, 1, or 0, which was an overspecification,
as noted by Emre Hasegeli. All of the existing callers appear to be fine
with the actual behavior, so just fix the comment.
In passing, improve infelicitous formatting of some call sites.
2014-01-04 20:01:51 +01: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
|
|
|
}
|
2003-06-25 00:21:24 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(false);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
Add an in-core GiST index opclass for inet/cidr types.
This operator class can accelerate subnet/supernet tests as well as
btree-equivalent ordered comparisons. It also handles a new network
operator inet && inet (overlaps, a/k/a "is supernet or subnet of"),
which is expected to be useful in exclusion constraints.
Ideally this opclass would be the default for GiST with inet/cidr data,
but we can't mark it that way until we figure out how to do a more or
less graceful transition from the current situation, in which the
really-completely-bogus inet/cidr opclasses in contrib/btree_gist are
marked as default. Having the opclass in core and not default is better
than not having it at all, though.
While at it, add new documentation sections to allow us to officially
document GiST/GIN/SP-GiST opclasses, something there was never a clear
place to do before. I filled these in with some simple tables listing
the existing opclasses and the operators they support, but there's
certainly scope to put more information there.
Emre Hasegeli, reviewed by Andreas Karlsson, further hacking by me
2014-04-08 21:46:14 +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);
|
|
|
|
}
|
|
|
|
|
2019-02-12 03:26:08 +01:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-02-21 19:49:54 +01:00
|
|
|
/*
|
2000-08-04 01:07:51 +02:00
|
|
|
* Extract data from a network datatype.
|
2000-02-21 19:49:54 +01:00
|
|
|
*/
|
2000-07-06 07:48:31 +02:00
|
|
|
Datum
|
|
|
|
network_host(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2003-06-25 00:21:24 +02:00
|
|
|
char *ptr;
|
|
|
|
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
|
1998-10-22 22:40:50 +02:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
/* force display of max bits, regardless of masklen... */
|
|
|
|
if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
|
2003-08-04 02:43:34 +02:00
|
|
|
tmp, sizeof(tmp)) == NULL)
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
|
|
|
errmsg("could not format inet value: %m")));
|
1999-03-22 06:00:57 +01:00
|
|
|
|
2000-11-10 21:13:27 +01:00
|
|
|
/* Suppress /n if present (shouldn't happen now) */
|
1998-10-22 22:40:50 +02:00
|
|
|
if ((ptr = strchr(tmp, '/')) != NULL)
|
2000-07-06 07:48:31 +02:00
|
|
|
*ptr = '\0';
|
|
|
|
|
2008-03-25 23:42:46 +01:00
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(tmp));
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2007-06-05 23:31:09 +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.
|
|
|
|
*/
|
2000-11-10 21:13:27 +01:00
|
|
|
Datum
|
|
|
|
network_show(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2000-11-10 21:13:27 +01:00
|
|
|
int len;
|
2003-06-25 00:21:24 +02:00
|
|
|
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
|
2000-11-10 21:13:27 +01:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
|
2003-08-04 02:43:34 +02:00
|
|
|
tmp, sizeof(tmp)) == NULL)
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
|
|
|
errmsg("could not format inet value: %m")));
|
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
/* Add /n if not present (which it won't be) */
|
|
|
|
if (strchr(tmp, '/') == NULL)
|
2000-11-10 21:13:27 +01:00
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
len = strlen(tmp);
|
|
|
|
snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
|
2000-11-10 21:13:27 +01:00
|
|
|
}
|
|
|
|
|
2008-03-25 23:42:46 +01:00
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(tmp));
|
2000-12-22 19:00:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
2006-01-26 03:35:51 +01:00
|
|
|
inet_abbrev(PG_FUNCTION_ARGS)
|
2000-12-22 19:00:24 +01:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2000-12-22 19:00:24 +01:00
|
|
|
char *dst;
|
2003-06-25 00:21:24 +02:00
|
|
|
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
|
2000-12-22 19:00:24 +01:00
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
dst = inet_net_ntop(ip_family(ip), ip_addr(ip),
|
|
|
|
ip_bits(ip), tmp, sizeof(tmp));
|
2003-06-25 00:21:24 +02:00
|
|
|
|
|
|
|
if (dst == NULL)
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
|
|
|
errmsg("could not format inet value: %m")));
|
2000-11-10 21:13:27 +01:00
|
|
|
|
2008-03-25 23:42:46 +01:00
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(tmp));
|
2000-11-10 21:13:27 +01:00
|
|
|
}
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
Datum
|
|
|
|
cidr_abbrev(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2006-01-26 03:35:51 +01:00
|
|
|
char *dst;
|
|
|
|
char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
|
|
|
|
|
|
|
|
dst = 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")));
|
|
|
|
|
2008-03-25 23:42:46 +01:00
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(tmp));
|
2006-01-26 03:35:51 +01:00
|
|
|
}
|
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
Datum
|
|
|
|
network_masklen(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
1999-03-22 06:00:57 +01:00
|
|
|
|
2000-08-04 01:07:51 +02:00
|
|
|
PG_RETURN_INT32(ip_bits(ip));
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
Datum
|
|
|
|
network_family(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
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;
|
2003-06-25 00:21:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-07-06 07:48:31 +02:00
|
|
|
Datum
|
|
|
|
network_broadcast(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2000-11-10 21:13:27 +01:00
|
|
|
inet *dst;
|
2011-04-10 17:42:00 +02:00
|
|
|
int byte;
|
2003-08-04 02:43:34 +02:00
|
|
|
int bits;
|
|
|
|
int maxbytes;
|
2003-06-25 00:21:24 +02:00
|
|
|
unsigned char mask;
|
2003-08-04 02:43:34 +02:00
|
|
|
unsigned char *a,
|
|
|
|
*b;
|
2000-11-10 21:13:27 +01:00
|
|
|
|
|
|
|
/* make sure any unused bits are zeroed */
|
2007-04-06 06:21:44 +02:00
|
|
|
dst = (inet *) palloc0(sizeof(inet));
|
1998-10-22 22:40:50 +02:00
|
|
|
|
2016-08-23 15:39:54 +02:00
|
|
|
maxbytes = ip_addrsize(ip);
|
2003-06-25 00:21:24 +02:00
|
|
|
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)
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
mask = 0x00;
|
|
|
|
bits -= 8;
|
2003-08-04 02:43:34 +02:00
|
|
|
}
|
|
|
|
else if (bits == 0)
|
2003-06-25 00:21:24 +02:00
|
|
|
mask = 0xff;
|
2003-08-04 02:43:34 +02:00
|
|
|
else
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
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
|
|
|
|
2000-11-10 21:13:27 +01:00
|
|
|
ip_family(dst) = ip_family(ip);
|
|
|
|
ip_bits(dst) = ip_bits(ip);
|
2007-04-06 06:21:44 +02:00
|
|
|
SET_INET_VARSIZE(dst);
|
2000-07-06 07:48:31 +02:00
|
|
|
|
2000-11-10 21:13:27 +01:00
|
|
|
PG_RETURN_INET_P(dst);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-07-06 07:48:31 +02:00
|
|
|
Datum
|
|
|
|
network_network(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2000-11-10 21:13:27 +01:00
|
|
|
inet *dst;
|
2011-04-10 17:42:00 +02:00
|
|
|
int byte;
|
2003-08-04 02:43:34 +02:00
|
|
|
int bits;
|
2003-06-25 00:21:24 +02:00
|
|
|
unsigned char mask;
|
2003-08-04 02:43:34 +02:00
|
|
|
unsigned char *a,
|
|
|
|
*b;
|
2000-11-10 21:13:27 +01:00
|
|
|
|
|
|
|
/* make sure any unused bits are zeroed */
|
2007-04-06 06:21:44 +02:00
|
|
|
dst = (inet *) palloc0(sizeof(inet));
|
1998-10-22 22:40:50 +02:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
bits = ip_bits(ip);
|
|
|
|
a = ip_addr(ip);
|
|
|
|
b = ip_addr(dst);
|
|
|
|
|
|
|
|
byte = 0;
|
2009-06-11 16:49:15 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
while (bits)
|
|
|
|
{
|
|
|
|
if (bits >= 8)
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
mask = 0xff;
|
|
|
|
bits -= 8;
|
2003-08-04 02:43:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
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
|
|
|
|
2000-11-10 21:13:27 +01:00
|
|
|
ip_family(dst) = ip_family(ip);
|
|
|
|
ip_bits(dst) = ip_bits(ip);
|
2007-04-06 06:21:44 +02:00
|
|
|
SET_INET_VARSIZE(dst);
|
2000-11-10 21:13:27 +01:00
|
|
|
|
|
|
|
PG_RETURN_INET_P(dst);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2000-07-06 07:48:31 +02:00
|
|
|
Datum
|
|
|
|
network_netmask(PG_FUNCTION_ARGS)
|
1998-10-22 22:40:50 +02:00
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2000-11-10 21:13:27 +01:00
|
|
|
inet *dst;
|
2011-04-10 17:42:00 +02:00
|
|
|
int byte;
|
2003-08-04 02:43:34 +02:00
|
|
|
int bits;
|
2003-06-25 00:21:24 +02:00
|
|
|
unsigned char mask;
|
|
|
|
unsigned char *b;
|
2000-11-10 21:13:27 +01:00
|
|
|
|
|
|
|
/* make sure any unused bits are zeroed */
|
2007-04-06 06:21:44 +02:00
|
|
|
dst = (inet *) palloc0(sizeof(inet));
|
1998-10-22 22:40:50 +02:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
bits = ip_bits(ip);
|
|
|
|
b = ip_addr(dst);
|
|
|
|
|
|
|
|
byte = 0;
|
2009-06-11 16:49:15 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
while (bits)
|
|
|
|
{
|
|
|
|
if (bits >= 8)
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
mask = 0xff;
|
|
|
|
bits -= 8;
|
2003-08-04 02:43:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
mask = 0xff << (8 - bits);
|
|
|
|
bits = 0;
|
|
|
|
}
|
2000-11-10 21:13:27 +01:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
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
|
|
|
|
2000-11-10 21:13:27 +01:00
|
|
|
ip_family(dst) = ip_family(ip);
|
2003-12-01 19:50:19 +01:00
|
|
|
ip_bits(dst) = ip_maxbits(ip);
|
2007-04-06 06:21:44 +02:00
|
|
|
SET_INET_VARSIZE(dst);
|
2000-07-06 07:48:31 +02:00
|
|
|
|
2000-11-10 21:13:27 +01:00
|
|
|
PG_RETURN_INET_P(dst);
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
|
|
|
|
2003-03-21 22:54:29 +01:00
|
|
|
Datum
|
|
|
|
network_hostmask(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2003-03-21 22:54:29 +01:00
|
|
|
inet *dst;
|
2011-04-10 17:42:00 +02:00
|
|
|
int byte;
|
2003-08-04 02:43:34 +02:00
|
|
|
int bits;
|
|
|
|
int maxbytes;
|
2003-06-25 00:21:24 +02:00
|
|
|
unsigned char mask;
|
|
|
|
unsigned char *b;
|
2003-03-21 22:54:29 +01:00
|
|
|
|
|
|
|
/* make sure any unused bits are zeroed */
|
2007-04-06 06:21:44 +02:00
|
|
|
dst = (inet *) palloc0(sizeof(inet));
|
2003-03-21 22:54:29 +01:00
|
|
|
|
2016-08-23 15:39:54 +02:00
|
|
|
maxbytes = ip_addrsize(ip);
|
2003-06-25 00:21:24 +02:00
|
|
|
bits = ip_maxbits(ip) - ip_bits(ip);
|
|
|
|
b = ip_addr(dst);
|
|
|
|
|
|
|
|
byte = maxbytes - 1;
|
2009-06-11 16:49:15 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
while (bits)
|
|
|
|
{
|
|
|
|
if (bits >= 8)
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
mask = 0xff;
|
|
|
|
bits -= 8;
|
2003-08-04 02:43:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
mask = 0xff >> (8 - bits);
|
|
|
|
bits = 0;
|
|
|
|
}
|
2003-03-21 22:54:29 +01:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
b[byte] = mask;
|
2011-04-10 17:42:00 +02:00
|
|
|
byte--;
|
2003-08-04 02:43:34 +02:00
|
|
|
}
|
2003-03-21 22:54:29 +01:00
|
|
|
|
|
|
|
ip_family(dst) = ip_family(ip);
|
2003-12-01 19:50:19 +01:00
|
|
|
ip_bits(dst) = ip_maxbits(ip);
|
2007-04-06 06:21:44 +02:00
|
|
|
SET_INET_VARSIZE(dst);
|
2003-03-21 22:54:29 +01:00
|
|
|
|
|
|
|
PG_RETURN_INET_P(dst);
|
|
|
|
}
|
2001-06-10 00:16:18 +02:00
|
|
|
|
2015-05-05 20:22:24 +02:00
|
|
|
/*
|
|
|
|
* 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),
|
2016-08-23 15:39:54 +02:00
|
|
|
*a2 = PG_GETARG_INET_PP(1);
|
2015-05-05 20:22:24 +02:00
|
|
|
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)));
|
|
|
|
|
2016-08-23 15:39:54 +02:00
|
|
|
PG_RETURN_INET_P(cidr_set_masklen_internal(a1, commonbits));
|
2015-05-05 20:22:24 +02:00
|
|
|
}
|
|
|
|
|
2001-06-10 00:16:18 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
2001-06-10 00:16:18 +02:00
|
|
|
*/
|
|
|
|
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)
|
2001-06-10 00:16:18 +02:00
|
|
|
{
|
|
|
|
switch (typid)
|
|
|
|
{
|
|
|
|
case INETOID:
|
|
|
|
case CIDROID:
|
2001-10-25 07:50:21 +02:00
|
|
|
{
|
2011-11-08 21:39:43 +01:00
|
|
|
inet *ip = DatumGetInetPP(value);
|
2003-08-04 02:43:34 +02:00
|
|
|
int len;
|
|
|
|
double res;
|
|
|
|
int i;
|
2003-06-25 00:21:24 +02:00
|
|
|
|
|
|
|
/*
|
2006-01-26 03:35:51 +01:00
|
|
|
* Note that we don't use the full address for IPv6.
|
2003-06-25 00:21:24 +02:00
|
|
|
*/
|
|
|
|
if (ip_family(ip) == PGSQL_AF_INET)
|
|
|
|
len = 4;
|
2001-10-25 07:50:21 +02:00
|
|
|
else
|
2003-06-25 00:21:24 +02:00
|
|
|
len = 5;
|
|
|
|
|
|
|
|
res = ip_family(ip);
|
2003-08-04 02:43:34 +02:00
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
res *= 256;
|
|
|
|
res += ip_addr(ip)[i];
|
|
|
|
}
|
|
|
|
return res;
|
2001-10-25 07:50:21 +02:00
|
|
|
}
|
2001-06-10 00:16:18 +02:00
|
|
|
case MACADDROID:
|
2001-10-25 07:50:21 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2017-03-15 16:16:25 +01:00
|
|
|
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;
|
|
|
|
}
|
2001-06-10 00:16:18 +02:00
|
|
|
}
|
|
|
|
|
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;
|
2001-06-10 00:16:18 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1998-10-22 22:40:50 +02:00
|
|
|
/*
|
2003-06-25 00:21:24 +02:00
|
|
|
* int
|
|
|
|
* bitncmp(l, r, n)
|
2003-08-04 02:43:34 +02:00
|
|
|
* compare bit masks l and r, for n bits.
|
2003-06-25 00:21:24 +02:00
|
|
|
* return:
|
Fix header comment for bitncmp().
The result is an int less than, equal to, or greater than zero, in the
style of memcmp (and, in fact, exactly the output of memcmp in some cases).
This comment previously said -1, 1, or 0, which was an overspecification,
as noted by Emre Hasegeli. All of the existing callers appear to be fine
with the actual behavior, so just fix the comment.
In passing, improve infelicitous formatting of some call sites.
2014-01-04 20:01:51 +01:00
|
|
|
* <0, >0, or 0 in the libc tradition.
|
2003-06-25 00:21:24 +02:00
|
|
|
* 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.
|
2003-06-25 00:21:24 +02:00
|
|
|
* author:
|
2003-08-04 02:43:34 +02:00
|
|
|
* Paul Vixie (ISC), June 1996
|
1998-10-22 22:40:50 +02:00
|
|
|
*/
|
Add an in-core GiST index opclass for inet/cidr types.
This operator class can accelerate subnet/supernet tests as well as
btree-equivalent ordered comparisons. It also handles a new network
operator inet && inet (overlaps, a/k/a "is supernet or subnet of"),
which is expected to be useful in exclusion constraints.
Ideally this opclass would be the default for GiST with inet/cidr data,
but we can't mark it that way until we figure out how to do a more or
less graceful transition from the current situation, in which the
really-completely-bogus inet/cidr opclasses in contrib/btree_gist are
marked as default. Having the opclass in core and not default is better
than not having it at all, though.
While at it, add new documentation sections to allow us to officially
document GiST/GIN/SP-GiST opclasses, something there was never a clear
place to do before. I filled these in with some simple tables listing
the existing opclasses and the operators they support, but there's
certainly scope to put more information there.
Emre Hasegeli, reviewed by Andreas Karlsson, further hacking by me
2014-04-08 21:46:14 +02:00
|
|
|
int
|
|
|
|
bitncmp(const unsigned char *l, const unsigned char *r, int n)
|
2003-06-25 00:21:24 +02:00
|
|
|
{
|
Add an in-core GiST index opclass for inet/cidr types.
This operator class can accelerate subnet/supernet tests as well as
btree-equivalent ordered comparisons. It also handles a new network
operator inet && inet (overlaps, a/k/a "is supernet or subnet of"),
which is expected to be useful in exclusion constraints.
Ideally this opclass would be the default for GiST with inet/cidr data,
but we can't mark it that way until we figure out how to do a more or
less graceful transition from the current situation, in which the
really-completely-bogus inet/cidr opclasses in contrib/btree_gist are
marked as default. Having the opclass in core and not default is better
than not having it at all, though.
While at it, add new documentation sections to allow us to officially
document GiST/GIN/SP-GiST opclasses, something there was never a clear
place to do before. I filled these in with some simple tables listing
the existing opclasses and the operators they support, but there's
certainly scope to put more information there.
Emre Hasegeli, reviewed by Andreas Karlsson, further hacking by me
2014-04-08 21:46:14 +02:00
|
|
|
unsigned int lb,
|
2003-08-04 02:43:34 +02:00
|
|
|
rb;
|
|
|
|
int x,
|
|
|
|
b;
|
2003-06-25 00:21:24 +02:00
|
|
|
|
|
|
|
b = n / 8;
|
|
|
|
x = memcmp(l, r, b);
|
2009-10-08 06:46:21 +02:00
|
|
|
if (x || (n % 8) == 0)
|
2006-01-11 09:43:13 +01:00
|
|
|
return x;
|
2003-06-25 00:21:24 +02:00
|
|
|
|
Add an in-core GiST index opclass for inet/cidr types.
This operator class can accelerate subnet/supernet tests as well as
btree-equivalent ordered comparisons. It also handles a new network
operator inet && inet (overlaps, a/k/a "is supernet or subnet of"),
which is expected to be useful in exclusion constraints.
Ideally this opclass would be the default for GiST with inet/cidr data,
but we can't mark it that way until we figure out how to do a more or
less graceful transition from the current situation, in which the
really-completely-bogus inet/cidr opclasses in contrib/btree_gist are
marked as default. Having the opclass in core and not default is better
than not having it at all, though.
While at it, add new documentation sections to allow us to officially
document GiST/GIN/SP-GiST opclasses, something there was never a clear
place to do before. I filled these in with some simple tables listing
the existing opclasses and the operators they support, but there's
certainly scope to put more information there.
Emre Hasegeli, reviewed by Andreas Karlsson, further hacking by me
2014-04-08 21:46:14 +02:00
|
|
|
lb = l[b];
|
|
|
|
rb = r[b];
|
2003-08-04 02:43:34 +02:00
|
|
|
for (b = n % 8; b > 0; b--)
|
|
|
|
{
|
2005-12-25 03:14:19 +01:00
|
|
|
if (IS_HIGHBIT_SET(lb) != IS_HIGHBIT_SET(rb))
|
2003-08-04 02:43:34 +02:00
|
|
|
{
|
2005-12-25 03:14:19 +01:00
|
|
|
if (IS_HIGHBIT_SET(lb))
|
|
|
|
return 1;
|
|
|
|
return -1;
|
2003-06-25 00:21:24 +02:00
|
|
|
}
|
|
|
|
lb <<= 1;
|
|
|
|
rb <<= 1;
|
|
|
|
}
|
2005-12-25 03:14:19 +01:00
|
|
|
return 0;
|
1998-10-22 22:40:50 +02:00
|
|
|
}
|
2000-10-27 03:55:23 +02:00
|
|
|
|
Add an in-core GiST index opclass for inet/cidr types.
This operator class can accelerate subnet/supernet tests as well as
btree-equivalent ordered comparisons. It also handles a new network
operator inet && inet (overlaps, a/k/a "is supernet or subnet of"),
which is expected to be useful in exclusion constraints.
Ideally this opclass would be the default for GiST with inet/cidr data,
but we can't mark it that way until we figure out how to do a more or
less graceful transition from the current situation, in which the
really-completely-bogus inet/cidr opclasses in contrib/btree_gist are
marked as default. Having the opclass in core and not default is better
than not having it at all, though.
While at it, add new documentation sections to allow us to officially
document GiST/GIN/SP-GiST opclasses, something there was never a clear
place to do before. I filled these in with some simple tables listing
the existing opclasses and the operators they support, but there's
certainly scope to put more information there.
Emre Hasegeli, reviewed by Andreas Karlsson, further hacking by me
2014-04-08 21:46:14 +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)
|
|
|
|
*/
|
2000-10-27 03:55:23 +02:00
|
|
|
static bool
|
2003-06-25 00:21:24 +02:00
|
|
|
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;
|
2003-06-25 00:21:24 +02:00
|
|
|
unsigned char mask;
|
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
if (family == PGSQL_AF_INET)
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
maxbits = 32;
|
|
|
|
maxbytes = 4;
|
2003-08-04 02:43:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-06-25 00:21:24 +02:00
|
|
|
maxbits = 128;
|
|
|
|
maxbytes = 16;
|
|
|
|
}
|
2003-08-02 01:22:52 +02:00
|
|
|
Assert(bits <= maxbits);
|
2003-06-25 00:21:24 +02:00
|
|
|
|
|
|
|
if (bits == maxbits)
|
2003-08-02 01:22:52 +02:00
|
|
|
return true;
|
2003-06-25 00:21:24 +02:00
|
|
|
|
2004-10-08 03:10:31 +02:00
|
|
|
byte = bits / 8;
|
2009-06-11 16:49:15 +02:00
|
|
|
|
2003-06-25 00:21:24 +02:00
|
|
|
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;
|
2003-06-25 00:21:24 +02:00
|
|
|
mask = 0xff;
|
2011-04-10 17:42:00 +02:00
|
|
|
byte++;
|
2003-06-25 00:21:24 +02:00
|
|
|
}
|
2000-10-27 03:55:23 +02:00
|
|
|
|
2003-08-02 01:22:52 +02:00
|
|
|
return true;
|
2000-10-27 03:55:23 +02:00
|
|
|
}
|
2001-06-17 04:05:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* return "last" IP on a given network. It's the broadcast address,
|
2017-03-23 16:29:42 +01:00
|
|
|
* however, masklen has to be set to its max bits, since
|
2001-06-17 04:05:20 +02:00
|
|
|
* 192.168.0.255/24 is considered less than 192.168.0.255/32
|
|
|
|
*
|
2003-06-25 00:21:24 +02:00
|
|
|
* inet_set_masklen() hacked to max out the masklength to 128 for IPv6
|
|
|
|
* and 32 for IPv4 when given '-1' as argument.
|
2001-06-17 04:05:20 +02:00
|
|
|
*/
|
|
|
|
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));
|
2001-06-17 04:05:20 +02:00
|
|
|
}
|
2004-06-13 21:56:52 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2004-06-13 21:56:52 +02:00
|
|
|
|
|
|
|
if (port == NULL)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
switch (port->raddr.addr.ss_family)
|
|
|
|
{
|
|
|
|
case AF_INET:
|
2004-06-13 21:56:52 +02:00
|
|
|
#ifdef HAVE_IPV6
|
2004-08-29 07:07:03 +02:00
|
|
|
case AF_INET6:
|
2004-06-13 21:56:52 +02:00
|
|
|
#endif
|
2004-08-29 07:07:03 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PG_RETURN_NULL();
|
2004-06-13 21:56:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
remote_host[0] = '\0';
|
|
|
|
|
2005-10-17 18:24:20 +02:00
|
|
|
ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
|
|
|
|
remote_host, sizeof(remote_host),
|
|
|
|
NULL, 0,
|
|
|
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
2011-08-09 17:28:35 +02:00
|
|
|
if (ret != 0)
|
2004-06-13 21:56:52 +02:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2007-05-18 01:31:49 +02:00
|
|
|
clean_ipv6_addr(port->raddr.addr.ss_family, remote_host);
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
PG_RETURN_INET_P(network_in(remote_host, false));
|
2004-06-13 21:56:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2004-06-13 21:56:52 +02:00
|
|
|
|
|
|
|
if (port == NULL)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
switch (port->raddr.addr.ss_family)
|
|
|
|
{
|
|
|
|
case AF_INET:
|
2004-06-13 21:56:52 +02:00
|
|
|
#ifdef HAVE_IPV6
|
2004-08-29 07:07:03 +02:00
|
|
|
case AF_INET6:
|
2004-06-13 21:56:52 +02:00
|
|
|
#endif
|
2004-08-29 07:07:03 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PG_RETURN_NULL();
|
2004-06-13 21:56:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
remote_port[0] = '\0';
|
|
|
|
|
2005-10-17 18:24:20 +02:00
|
|
|
ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
|
|
|
|
NULL, 0,
|
|
|
|
remote_port, sizeof(remote_port),
|
|
|
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
2011-08-09 17:28:35 +02:00
|
|
|
if (ret != 0)
|
2004-06-13 21:56:52 +02:00
|
|
|
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;
|
2004-06-13 21:56:52 +02:00
|
|
|
|
|
|
|
if (port == NULL)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
switch (port->laddr.addr.ss_family)
|
|
|
|
{
|
|
|
|
case AF_INET:
|
2004-06-13 21:56:52 +02:00
|
|
|
#ifdef HAVE_IPV6
|
2004-08-29 07:07:03 +02:00
|
|
|
case AF_INET6:
|
2004-06-13 21:56:52 +02:00
|
|
|
#endif
|
2004-08-29 07:07:03 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PG_RETURN_NULL();
|
2004-06-13 21:56:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
local_host[0] = '\0';
|
|
|
|
|
2005-10-17 18:24:20 +02:00
|
|
|
ret = pg_getnameinfo_all(&port->laddr.addr, port->laddr.salen,
|
|
|
|
local_host, sizeof(local_host),
|
|
|
|
NULL, 0,
|
|
|
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
2011-08-09 17:28:35 +02:00
|
|
|
if (ret != 0)
|
2004-06-13 21:56:52 +02:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2007-05-18 01:31:49 +02:00
|
|
|
clean_ipv6_addr(port->laddr.addr.ss_family, local_host);
|
|
|
|
|
2006-01-26 03:35:51 +01:00
|
|
|
PG_RETURN_INET_P(network_in(local_host, false));
|
2004-06-13 21:56:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2004-06-13 21:56:52 +02:00
|
|
|
|
|
|
|
if (port == NULL)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
switch (port->laddr.addr.ss_family)
|
|
|
|
{
|
|
|
|
case AF_INET:
|
2004-06-13 21:56:52 +02:00
|
|
|
#ifdef HAVE_IPV6
|
2004-08-29 07:07:03 +02:00
|
|
|
case AF_INET6:
|
2004-06-13 21:56:52 +02:00
|
|
|
#endif
|
2004-08-29 07:07:03 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PG_RETURN_NULL();
|
2004-06-13 21:56:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
local_port[0] = '\0';
|
|
|
|
|
2005-10-17 18:24:20 +02:00
|
|
|
ret = pg_getnameinfo_all(&port->laddr.addr, port->laddr.salen,
|
|
|
|
NULL, 0,
|
|
|
|
local_port, sizeof(local_port),
|
|
|
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
2011-08-09 17:28:35 +02:00
|
|
|
if (ret != 0)
|
2004-06-13 21:56:52 +02:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
PG_RETURN_DATUM(DirectFunctionCall1(int4in, CStringGetDatum(local_port)));
|
|
|
|
}
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
inetnot(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2006-02-11 04:32:41 +01:00
|
|
|
inet *dst;
|
|
|
|
|
2007-04-06 06:21:44 +02:00
|
|
|
dst = (inet *) palloc0(sizeof(inet));
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
{
|
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);
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
while (nb-- > 0)
|
|
|
|
pdst[nb] = ~pip[nb];
|
|
|
|
}
|
|
|
|
ip_bits(dst) = ip_bits(ip);
|
|
|
|
|
|
|
|
ip_family(dst) = ip_family(ip);
|
2007-04-06 06:21:44 +02:00
|
|
|
SET_INET_VARSIZE(dst);
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
PG_RETURN_INET_P(dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
inetand(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
|
|
|
inet *ip2 = PG_GETARG_INET_PP(1);
|
2006-02-11 04:32:41 +01:00
|
|
|
inet *dst;
|
|
|
|
|
2007-04-06 06:21:44 +02:00
|
|
|
dst = (inet *) palloc0(sizeof(inet));
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
if (ip_family(ip) != ip_family(ip2))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
2006-02-11 21:39:59 +01:00
|
|
|
errmsg("cannot AND inet values of different sizes")));
|
2006-02-11 04:32:41 +01:00
|
|
|
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);
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
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);
|
2007-04-06 06:21:44 +02:00
|
|
|
SET_INET_VARSIZE(dst);
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
PG_RETURN_INET_P(dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
inetor(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
|
|
|
inet *ip2 = PG_GETARG_INET_PP(1);
|
2006-02-11 04:32:41 +01:00
|
|
|
inet *dst;
|
|
|
|
|
2007-04-06 06:21:44 +02:00
|
|
|
dst = (inet *) palloc0(sizeof(inet));
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
if (ip_family(ip) != ip_family(ip2))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
2006-02-11 21:39:59 +01:00
|
|
|
errmsg("cannot OR inet values of different sizes")));
|
2006-02-11 04:32:41 +01:00
|
|
|
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);
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
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);
|
2007-04-06 06:21:44 +02:00
|
|
|
SET_INET_VARSIZE(dst);
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
PG_RETURN_INET_P(dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-11 21:39:59 +01:00
|
|
|
static inet *
|
|
|
|
internal_inetpl(inet *ip, int64 addend)
|
2006-02-11 04:32:41 +01:00
|
|
|
{
|
|
|
|
inet *dst;
|
|
|
|
|
2007-04-06 06:21:44 +02:00
|
|
|
dst = (inet *) palloc0(sizeof(inet));
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
{
|
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;
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
while (nb-- > 0)
|
|
|
|
{
|
2006-02-11 21:39:59 +01:00
|
|
|
carry = pip[nb] + (int) (addend & 0xFF) + carry;
|
|
|
|
pdst[nb] = (unsigned char) (carry & 0xFF);
|
|
|
|
carry >>= 8;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-02-11 21:39:59 +01: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.
|
2006-02-11 21:39:59 +01:00
|
|
|
*/
|
|
|
|
addend &= ~((int64) 0xFF);
|
|
|
|
addend /= 0x100;
|
2006-02-11 04:32:41 +01:00
|
|
|
}
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-02-11 21:39:59 +01: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.
|
2006-02-11 21:39:59 +01:00
|
|
|
*/
|
|
|
|
if (!((addend == 0 && carry == 0) ||
|
|
|
|
(addend == -1 && carry == 1)))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
2007-01-02 23:21:08 +01:00
|
|
|
errmsg("result is out of range")));
|
2006-02-11 04:32:41 +01:00
|
|
|
}
|
|
|
|
|
2007-04-06 06:21:44 +02:00
|
|
|
ip_bits(dst) = ip_bits(ip);
|
2006-02-11 04:32:41 +01:00
|
|
|
ip_family(dst) = ip_family(ip);
|
2007-04-06 06:21:44 +02:00
|
|
|
SET_INET_VARSIZE(dst);
|
2006-02-11 04:32:41 +01:00
|
|
|
|
2006-02-11 21:39:59 +01:00
|
|
|
return dst;
|
2006-02-11 04:32:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
inetpl(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2006-10-04 02:30:14 +02:00
|
|
|
int64 addend = PG_GETARG_INT64(1);
|
2006-02-11 04:32:41 +01:00
|
|
|
|
2006-02-11 21:39:59 +01:00
|
|
|
PG_RETURN_INET_P(internal_inetpl(ip, addend));
|
2006-02-11 04:32:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
inetmi_int8(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
2006-10-04 02:30:14 +02:00
|
|
|
int64 addend = PG_GETARG_INT64(1);
|
2006-02-11 04:32:41 +01:00
|
|
|
|
2006-02-11 21:39:59 +01:00
|
|
|
PG_RETURN_INET_P(internal_inetpl(ip, -addend));
|
2006-02-11 04:32:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
inetmi(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2011-12-12 08:49:47 +01:00
|
|
|
inet *ip = PG_GETARG_INET_PP(0);
|
|
|
|
inet *ip2 = PG_GETARG_INET_PP(1);
|
2006-02-11 04:32:41 +01:00
|
|
|
int64 res = 0;
|
|
|
|
|
|
|
|
if (ip_family(ip) != ip_family(ip2))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
2006-02-11 21:39:59 +01:00
|
|
|
errmsg("cannot subtract inet values of different sizes")));
|
2006-02-11 04:32:41 +01:00
|
|
|
else
|
|
|
|
{
|
2006-02-11 21:39:59 +01:00
|
|
|
/*
|
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
|
2014-05-06 18:12:18 +02:00
|
|
|
* 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-02-11 21:39:59 +01:00
|
|
|
*/
|
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;
|
2006-02-11 04:32:41 +01:00
|
|
|
|
|
|
|
while (nb-- > 0)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
int lobyte;
|
2006-02-11 21:39:59 +01:00
|
|
|
|
|
|
|
carry = pip[nb] + (~pip2[nb] & 0xFF) + carry;
|
|
|
|
lobyte = carry & 0xFF;
|
2011-04-10 17:42:00 +02:00
|
|
|
if (byte < sizeof(int64))
|
2006-02-11 04:32:41 +01:00
|
|
|
{
|
2011-04-10 17:42:00 +02:00
|
|
|
res |= ((int64) lobyte) << (byte * 8);
|
2006-02-11 21:39:59 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* 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.
|
2006-02-11 21:39:59 +01:00
|
|
|
*/
|
|
|
|
if ((res < 0) ? (lobyte != 0xFF) : (lobyte != 0))
|
2006-02-11 04:32:41 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
2007-01-02 23:21:08 +01:00
|
|
|
errmsg("result is out of range")));
|
2006-02-11 04:32:41 +01:00
|
|
|
}
|
2006-02-11 21:39:59 +01:00
|
|
|
carry >>= 8;
|
2011-04-10 17:42:00 +02:00
|
|
|
byte++;
|
2006-02-11 04:32:41 +01:00
|
|
|
}
|
2006-02-11 21:39:59 +01:00
|
|
|
|
|
|
|
/*
|
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.
|
2006-02-11 21:39:59 +01:00
|
|
|
*/
|
2011-04-10 17:42:00 +02:00
|
|
|
if (carry == 0 && byte < sizeof(int64))
|
2018-06-17 22:15:11 +02:00
|
|
|
res |= ((uint64) (int64) -1) << (byte * 8);
|
2006-02-11 04:32:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
PG_RETURN_INT64(res);
|
|
|
|
}
|
2007-05-18 01:31:49 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2014-05-06 18:12:18 +02:00
|
|
|
* values. Since the result of getnameinfo() might include a zone spec,
|
2007-05-18 01:31:49 +02:00
|
|
|
* call this to remove it anywhere we want to feed getnameinfo's output to
|
2014-05-06 18:12:18 +02:00
|
|
|
* network_in. Beats failing entirely.
|
2007-05-18 01:31:49 +02:00
|
|
|
*
|
|
|
|
* 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, '%');
|
2007-05-18 01:31:49 +02:00
|
|
|
|
|
|
|
if (pct)
|
|
|
|
*pct = '\0';
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|