Commit fbb1daed authored by Tom Lane's avatar Tom Lane

Fix incorrect addition, subtraction, and overflow checking in new

inet operators.
parent 77bb65d3
/* /*
* PostgreSQL type definitions for the INET and CIDR types. * PostgreSQL type definitions for the INET and CIDR types.
* *
* $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.64 2006/02/11 03:32:39 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.65 2006/02/11 20:39:58 tgl Exp $
* *
* Jon Postel RIP 16 Oct 1998 * Jon Postel RIP 16 Oct 1998
*/ */
...@@ -27,7 +27,7 @@ static int32 network_cmp_internal(inet *a1, inet *a2); ...@@ -27,7 +27,7 @@ static int32 network_cmp_internal(inet *a1, inet *a2);
static int bitncmp(void *l, void *r, int n); static int bitncmp(void *l, void *r, int n);
static bool addressOK(unsigned char *a, int bits, int family); static bool addressOK(unsigned char *a, int bits, int family);
static int ip_addrsize(inet *inetptr); static int ip_addrsize(inet *inetptr);
static Datum internal_inetpl(inet *ip, int64 iarg); static inet *internal_inetpl(inet *ip, int64 addend);
/* /*
* Access macros. * Access macros.
...@@ -1292,8 +1292,7 @@ inetand(PG_FUNCTION_ARGS) ...@@ -1292,8 +1292,7 @@ inetand(PG_FUNCTION_ARGS)
if (ip_family(ip) != ip_family(ip2)) if (ip_family(ip) != ip_family(ip2))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("mismatch in address family (%d) != (%d)", errmsg("cannot AND inet values of different sizes")));
ip_family(ip), ip_family(ip2))));
else else
{ {
int nb = ip_addrsize(ip); int nb = ip_addrsize(ip);
...@@ -1327,8 +1326,7 @@ inetor(PG_FUNCTION_ARGS) ...@@ -1327,8 +1326,7 @@ inetor(PG_FUNCTION_ARGS)
if (ip_family(ip) != ip_family(ip2)) if (ip_family(ip) != ip_family(ip2))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("mismatch in address family (%d) != (%d)", errmsg("cannot OR inet values of different sizes")));
ip_family(ip), ip_family(ip2))));
else else
{ {
int nb = ip_addrsize(ip); int nb = ip_addrsize(ip);
...@@ -1350,8 +1348,8 @@ inetor(PG_FUNCTION_ARGS) ...@@ -1350,8 +1348,8 @@ inetor(PG_FUNCTION_ARGS)
} }
static Datum static inet *
internal_inetpl(inet *ip, int64 plus) internal_inetpl(inet *ip, int64 addend)
{ {
inet *dst; inet *dst;
...@@ -1365,15 +1363,31 @@ internal_inetpl(inet *ip, int64 plus) ...@@ -1365,15 +1363,31 @@ internal_inetpl(inet *ip, int64 plus)
while (nb-- > 0) while (nb-- > 0)
{ {
pdst[nb] = carry = pip[nb] + plus + carry; carry = pip[nb] + (int) (addend & 0xFF) + carry;
plus /= 0x100; /* process next byte */ pdst[nb] = (unsigned char) (carry & 0xFF);
carry /= 0x100; /* remove low byte */ carry >>= 8;
/* Overflow on high byte? */ /*
if (nb == 0 && (plus != 0 || carry != 0)) * We have to be careful about right-shifting addend because
ereport(ERROR, * right-shift isn't portable for negative values, while
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), * simply dividing by 256 doesn't work (the standard rounding
errmsg("result out of range"))); * is in the wrong direction, besides which there may be machines
* out there that round the wrong way). So, explicitly clear
* the low-order byte to remove any doubt about the correct
* result of the division, and then divide rather than shift.
*/
addend &= ~((int64) 0xFF);
addend /= 0x100;
} }
/*
* At this point we should have addend and carry both zero if
* original addend was >= 0, or addend -1 and carry 1 if original
* addend was < 0. Anything else means overflow.
*/
if (!((addend == 0 && carry == 0) ||
(addend == -1 && carry == 1)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("result out of range")));
} }
ip_bits(dst) = ip_bits(ip); ip_bits(dst) = ip_bits(ip);
...@@ -1382,7 +1396,7 @@ internal_inetpl(inet *ip, int64 plus) ...@@ -1382,7 +1396,7 @@ internal_inetpl(inet *ip, int64 plus)
((char *) ip_addr(dst) - (char *) VARDATA(dst)) + ((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
ip_addrsize(dst); ip_addrsize(dst);
PG_RETURN_INET_P(dst); return dst;
} }
...@@ -1390,9 +1404,9 @@ Datum ...@@ -1390,9 +1404,9 @@ Datum
inetpl(PG_FUNCTION_ARGS) inetpl(PG_FUNCTION_ARGS)
{ {
inet *ip = PG_GETARG_INET_P(0); inet *ip = PG_GETARG_INET_P(0);
int64 plus = PG_GETARG_INT64(1); int64 addend = PG_GETARG_INT64(1);
return internal_inetpl(ip, plus); PG_RETURN_INET_P(internal_inetpl(ip, addend));
} }
...@@ -1400,9 +1414,9 @@ Datum ...@@ -1400,9 +1414,9 @@ Datum
inetmi_int8(PG_FUNCTION_ARGS) inetmi_int8(PG_FUNCTION_ARGS)
{ {
inet *ip = PG_GETARG_INET_P(0); inet *ip = PG_GETARG_INET_P(0);
int64 plus = PG_GETARG_INT64(1); int64 addend = PG_GETARG_INT64(1);
return internal_inetpl(ip, -plus); PG_RETURN_INET_P(internal_inetpl(ip, -addend));
} }
...@@ -1416,42 +1430,53 @@ inetmi(PG_FUNCTION_ARGS) ...@@ -1416,42 +1430,53 @@ inetmi(PG_FUNCTION_ARGS)
if (ip_family(ip) != ip_family(ip2)) if (ip_family(ip) != ip_family(ip2))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("mismatch in address family (%d) != (%d)", errmsg("cannot subtract inet values of different sizes")));
ip_family(ip), ip_family(ip2))));
else 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 nb = ip_addrsize(ip);
int byte = 0; int byte = 0;
unsigned char *pip = ip_addr(ip); unsigned char *pip = ip_addr(ip);
unsigned char *pip2 = ip_addr(ip2); unsigned char *pip2 = ip_addr(ip2);
int carry = 1;
while (nb-- > 0) while (nb-- > 0)
{ {
/* int lobyte;
* Error if overflow on last byte. This test is tricky
* because if the subtraction == 128 and res is negative, or carry = pip[nb] + (~pip2[nb] & 0xFF) + carry;
* if subtraction == -128 and res is positive, the result lobyte = carry & 0xFF;
* would still fit in int64. if (byte < sizeof(int64))
*/
if (byte + 1 == sizeof(int64) &&
(pip[nb] - pip2[nb] >= 128 + (res < 0) ||
pip[nb] - pip2[nb] <= -128 - (res > 0)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("result out of range")));
if (byte >= sizeof(int64))
{ {
/* Error if bytes beyond int64 length differ. */ res |= ((int64) lobyte) << (byte * 8);
if (pip[nb] != pip2[nb]) }
else
{
/*
* Input wider than int64: check for overflow. All bytes
* to the left of what will fit should be 0 or 0xFF,
* depending on sign of the now-complete result.
*/
if ((res < 0) ? (lobyte != 0xFF) : (lobyte != 0))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("result out of range"))); errmsg("result out of range")));
} }
else carry >>= 8;
res += (int64)(pip[nb] - pip2[nb]) << (byte * 8);
byte++; 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 |= ((int64) -1) << (byte * 8);
} }
PG_RETURN_INT64(res); PG_RETURN_INT64(res);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.395 2006/02/11 03:32:39 momjian Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.396 2006/02/11 20:39:58 tgl Exp $
* *
* NOTES * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * The script catalog/genbki.sh reads this file and generates .bki
...@@ -2297,7 +2297,7 @@ DESCR("bitwise or"); ...@@ -2297,7 +2297,7 @@ DESCR("bitwise or");
DATA(insert OID = 1675 ( bitxor PGNSP PGUID 12 f f t f i 2 1560 "1560 1560" _null_ _null_ _null_ bitxor - _null_ )); DATA(insert OID = 1675 ( bitxor PGNSP PGUID 12 f f t f i 2 1560 "1560 1560" _null_ _null_ _null_ bitxor - _null_ ));
DESCR("bitwise exclusive or"); DESCR("bitwise exclusive or");
DATA(insert OID = 1676 ( bitnot PGNSP PGUID 12 f f t f i 1 1560 "1560" _null_ _null_ _null_ bitnot - _null_ )); DATA(insert OID = 1676 ( bitnot PGNSP PGUID 12 f f t f i 1 1560 "1560" _null_ _null_ _null_ bitnot - _null_ ));
DESCR("bitwise negation"); DESCR("bitwise not");
DATA(insert OID = 1677 ( bitshiftleft PGNSP PGUID 12 f f t f i 2 1560 "1560 23" _null_ _null_ _null_ bitshiftleft - _null_ )); DATA(insert OID = 1677 ( bitshiftleft PGNSP PGUID 12 f f t f i 2 1560 "1560 23" _null_ _null_ _null_ bitshiftleft - _null_ ));
DESCR("bitwise left shift"); DESCR("bitwise left shift");
DATA(insert OID = 1678 ( bitshiftright PGNSP PGUID 12 f f t f i 2 1560 "1560 23" _null_ _null_ _null_ bitshiftright - _null_ )); DATA(insert OID = 1678 ( bitshiftright PGNSP PGUID 12 f f t f i 2 1560 "1560 23" _null_ _null_ _null_ bitshiftright - _null_ ));
...@@ -2423,28 +2423,28 @@ DATA(insert OID = 1715 ( cidr PGNSP PGUID 12 f f t f i 1 650 "869" _null_ _n ...@@ -2423,28 +2423,28 @@ DATA(insert OID = 1715 ( cidr PGNSP PGUID 12 f f t f i 1 650 "869" _null_ _n
DESCR("coerce inet to cidr"); DESCR("coerce inet to cidr");
DATA(insert OID = 2196 ( inet_client_addr PGNSP PGUID 12 f f f f s 0 869 "" _null_ _null_ _null_ inet_client_addr - _null_ )); DATA(insert OID = 2196 ( inet_client_addr PGNSP PGUID 12 f f f f s 0 869 "" _null_ _null_ _null_ inet_client_addr - _null_ ));
DESCR("INET address of the client"); DESCR("inet address of the client");
DATA(insert OID = 2197 ( inet_client_port PGNSP PGUID 12 f f f f s 0 23 "" _null_ _null_ _null_ inet_client_port - _null_ )); DATA(insert OID = 2197 ( inet_client_port PGNSP PGUID 12 f f f f s 0 23 "" _null_ _null_ _null_ inet_client_port - _null_ ));
DESCR("client's port number for this connection"); DESCR("client's port number for this connection");
DATA(insert OID = 2198 ( inet_server_addr PGNSP PGUID 12 f f f f s 0 869 "" _null_ _null_ _null_ inet_server_addr - _null_ )); DATA(insert OID = 2198 ( inet_server_addr PGNSP PGUID 12 f f f f s 0 869 "" _null_ _null_ _null_ inet_server_addr - _null_ ));
DESCR("INET address of the server"); DESCR("inet address of the server");
DATA(insert OID = 2199 ( inet_server_port PGNSP PGUID 12 f f f f s 0 23 "" _null_ _null_ _null_ inet_server_port - _null_ )); DATA(insert OID = 2199 ( inet_server_port PGNSP PGUID 12 f f f f s 0 23 "" _null_ _null_ _null_ inet_server_port - _null_ ));
DESCR("server's port number for this connection"); DESCR("server's port number for this connection");
DATA(insert OID = 2627 ( inetnot PGNSP PGUID 12 f f t f i 1 869 "869" _null_ _null_ _null_ inetnot - _null_ )); DATA(insert OID = 2627 ( inetnot PGNSP PGUID 12 f f t f i 1 869 "869" _null_ _null_ _null_ inetnot - _null_ ));
DESCR("binary NOT"); DESCR("bitwise not");
DATA(insert OID = 2628 ( inetand PGNSP PGUID 12 f f t f i 2 869 "869 869" _null_ _null_ _null_ inetand - _null_ )); DATA(insert OID = 2628 ( inetand PGNSP PGUID 12 f f t f i 2 869 "869 869" _null_ _null_ _null_ inetand - _null_ ));
DESCR("binary AND"); DESCR("bitwise and");
DATA(insert OID = 2629 ( inetor PGNSP PGUID 12 f f t f i 2 869 "869 869" _null_ _null_ _null_ inetor - _null_ )); DATA(insert OID = 2629 ( inetor PGNSP PGUID 12 f f t f i 2 869 "869 869" _null_ _null_ _null_ inetor - _null_ ));
DESCR("binary OR"); DESCR("bitwise or");
DATA(insert OID = 2630 ( inetpl PGNSP PGUID 12 f f t f i 2 869 "869 20" _null_ _null_ _null_ inetpl - _null_ )); DATA(insert OID = 2630 ( inetpl PGNSP PGUID 12 f f t f i 2 869 "869 20" _null_ _null_ _null_ inetpl - _null_ ));
DESCR("add integer to INET value"); DESCR("add integer to inet value");
DATA(insert OID = 2631 ( int8pl_inet PGNSP PGUID 14 f f t f i 2 869 "20 869" _null_ _null_ _null_ "select $2 + $1" - _null_ )); DATA(insert OID = 2631 ( int8pl_inet PGNSP PGUID 14 f f t f i 2 869 "20 869" _null_ _null_ _null_ "select $2 + $1" - _null_ ));
DESCR("add integer to INET value"); DESCR("add integer to inet value");
DATA(insert OID = 2632 ( inetmi_int8 PGNSP PGUID 12 f f t f i 2 869 "869 20" _null_ _null_ _null_ inetmi_int8 - _null_ )); DATA(insert OID = 2632 ( inetmi_int8 PGNSP PGUID 12 f f t f i 2 869 "869 20" _null_ _null_ _null_ inetmi_int8 - _null_ ));
DESCR("subtract integer from INET value"); DESCR("subtract integer from inet value");
DATA(insert OID = 2633 ( inetmi PGNSP PGUID 12 f f t f i 2 20 "869 869" _null_ _null_ _null_ inetmi - _null_ )); DATA(insert OID = 2633 ( inetmi PGNSP PGUID 12 f f t f i 2 20 "869 869" _null_ _null_ _null_ inetmi - _null_ ));
DESCR("subtract INET values"); DESCR("subtract inet values");
DATA(insert OID = 1686 ( numeric PGNSP PGUID 12 f f t f i 1 1700 "25" _null_ _null_ _null_ text_numeric - _null_ )); DATA(insert OID = 1686 ( numeric PGNSP PGUID 12 f f t f i 1 1700 "25" _null_ _null_ _null_ text_numeric - _null_ ));
DESCR("(internal)"); DESCR("(internal)");
......
This diff is collapsed.
...@@ -62,14 +62,29 @@ CREATE INDEX inet_idx1 ON inet_tbl(i); ...@@ -62,14 +62,29 @@ CREATE INDEX inet_idx1 ON inet_tbl(i);
SET enable_seqscan TO off; SET enable_seqscan TO off;
SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr; SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr;
SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr; SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr;
SELECT ~i FROM inet_tbl;
SELECT i & c FROM inet_tbl;
SELECT i | c FROM inet_tbl;
SELECT i + 500 FROM inet_tbl;
SELECT i - 500 FROM inet_tbl;
SELECT i - c FROM inet_tbl;
SET enable_seqscan TO on; SET enable_seqscan TO on;
DROP INDEX inet_idx1; DROP INDEX inet_idx1;
-- simple tests of inet boolean and arithmetic operators
SELECT i, ~i AS "~i" FROM inet_tbl;
SELECT i, c, i & c AS "and" FROM inet_tbl;
SELECT i, c, i | c AS "or" FROM inet_tbl;
SELECT i, i + 500 AS "i+500" FROM inet_tbl;
SELECT i, i - 500 AS "i-500" FROM inet_tbl;
SELECT i, c, i - c AS "minus" FROM inet_tbl;
SELECT '127.0.0.1'::inet + 257;
SELECT ('127.0.0.1'::inet + 257) - 257;
SELECT '127::1'::inet + 257;
SELECT ('127::1'::inet + 257) - 257;
SELECT '127.0.0.2'::inet - ('127.0.0.2'::inet + 500);
SELECT '127.0.0.2'::inet - ('127.0.0.2'::inet - 500);
SELECT '127::2'::inet - ('127::2'::inet + 500);
SELECT '127::2'::inet - ('127::2'::inet - 500);
-- these should give overflow errors:
SELECT '127.0.0.1'::inet + 10000000000;
SELECT '127.0.0.1'::inet - 10000000000;
SELECT '126::1'::inet - '127::2'::inet;
SELECT '127::1'::inet - '126::2'::inet;
-- but not these
SELECT '127::1'::inet + 10000000000;
SELECT '127::1'::inet - '127::2'::inet;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment