diff options
Diffstat (limited to 'src/backend/utils/adt/network.c')
-rw-r--r-- | src/backend/utils/adt/network.c | 208 |
1 files changed, 207 insertions, 1 deletions
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c index c7ce7b6a307..64daefa0294 100644 --- a/src/backend/utils/adt/network.c +++ b/src/backend/utils/adt/network.c @@ -1,7 +1,7 @@ /* * PostgreSQL type definitions for the INET and CIDR types. * - * $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.63 2006/02/07 17:04:04 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.64 2006/02/11 03:32:39 momjian Exp $ * * Jon Postel RIP 16 Oct 1998 */ @@ -27,6 +27,7 @@ static int32 network_cmp_internal(inet *a1, inet *a2); static int bitncmp(void *l, void *r, int n); static bool addressOK(unsigned char *a, int bits, int family); static int ip_addrsize(inet *inetptr); +static Datum internal_inetpl(inet *ip, int64 iarg); /* * Access macros. @@ -1250,3 +1251,208 @@ inet_server_port(PG_FUNCTION_ARGS) PG_RETURN_DATUM(DirectFunctionCall1(int4in, CStringGetDatum(local_port))); } + + +Datum +inetnot(PG_FUNCTION_ARGS) +{ + inet *ip = PG_GETARG_INET_P(0); + inet *dst; + + dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); + + { + int nb = ip_addrsize(ip); + unsigned char *pip = ip_addr(ip); + unsigned char *pdst = ip_addr(dst); + + while (nb-- > 0) + pdst[nb] = ~pip[nb]; + } + ip_bits(dst) = ip_bits(ip); + + ip_family(dst) = ip_family(ip); + VARATT_SIZEP(dst) = VARHDRSZ + + ((char *) ip_addr(dst) - (char *) VARDATA(dst)) + + ip_addrsize(dst); + + PG_RETURN_INET_P(dst); +} + + +Datum +inetand(PG_FUNCTION_ARGS) +{ + inet *ip = PG_GETARG_INET_P(0); + inet *ip2 = PG_GETARG_INET_P(1); + inet *dst; + + dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); + + if (ip_family(ip) != ip_family(ip2)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("mismatch in address family (%d) != (%d)", + ip_family(ip), ip_family(ip2)))); + else + { + int nb = ip_addrsize(ip); + unsigned char *pip = ip_addr(ip); + unsigned char *pip2 = ip_addr(ip2); + unsigned char *pdst = ip_addr(dst); + + while (nb-- > 0) + pdst[nb] = pip[nb] & pip2[nb]; + } + ip_bits(dst) = Max(ip_bits(ip), ip_bits(ip2)); + + ip_family(dst) = ip_family(ip); + VARATT_SIZEP(dst) = VARHDRSZ + + ((char *) ip_addr(dst) - (char *) VARDATA(dst)) + + ip_addrsize(dst); + + PG_RETURN_INET_P(dst); +} + + +Datum +inetor(PG_FUNCTION_ARGS) +{ + inet *ip = PG_GETARG_INET_P(0); + inet *ip2 = PG_GETARG_INET_P(1); + inet *dst; + + dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); + + if (ip_family(ip) != ip_family(ip2)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("mismatch in address family (%d) != (%d)", + ip_family(ip), ip_family(ip2)))); + else + { + int nb = ip_addrsize(ip); + unsigned char *pip = ip_addr(ip); + unsigned char *pip2 = ip_addr(ip2); + unsigned char *pdst = ip_addr(dst); + + while (nb-- > 0) + pdst[nb] = pip[nb] | pip2[nb]; + } + ip_bits(dst) = Max(ip_bits(ip), ip_bits(ip2)); + + ip_family(dst) = ip_family(ip); + VARATT_SIZEP(dst) = VARHDRSZ + + ((char *) ip_addr(dst) - (char *) VARDATA(dst)) + + ip_addrsize(dst); + + PG_RETURN_INET_P(dst); +} + + +static Datum +internal_inetpl(inet *ip, int64 plus) +{ + inet *dst; + + dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); + + { + int nb = ip_addrsize(ip); + unsigned char *pip = ip_addr(ip); + unsigned char *pdst = ip_addr(dst); + int carry = 0; + + while (nb-- > 0) + { + pdst[nb] = carry = pip[nb] + plus + carry; + plus /= 0x100; /* process next byte */ + carry /= 0x100; /* remove low byte */ + /* Overflow on high byte? */ + if (nb == 0 && (plus != 0 || carry != 0)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("result out of range"))); + } + } + ip_bits(dst) = ip_bits(ip); + + ip_family(dst) = ip_family(ip); + VARATT_SIZEP(dst) = VARHDRSZ + + ((char *) ip_addr(dst) - (char *) VARDATA(dst)) + + ip_addrsize(dst); + + PG_RETURN_INET_P(dst); +} + + +Datum +inetpl(PG_FUNCTION_ARGS) +{ + inet *ip = PG_GETARG_INET_P(0); + int64 plus = PG_GETARG_INT64(1); + + return internal_inetpl(ip, plus); +} + + +Datum +inetmi_int8(PG_FUNCTION_ARGS) +{ + inet *ip = PG_GETARG_INET_P(0); + int64 plus = PG_GETARG_INT64(1); + + return internal_inetpl(ip, -plus); +} + + +Datum +inetmi(PG_FUNCTION_ARGS) +{ + inet *ip = PG_GETARG_INET_P(0); + inet *ip2 = PG_GETARG_INET_P(1); + int64 res = 0; + + if (ip_family(ip) != ip_family(ip2)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("mismatch in address family (%d) != (%d)", + ip_family(ip), ip_family(ip2)))); + else + { + int nb = ip_addrsize(ip); + int byte = 0; + unsigned char *pip = ip_addr(ip); + unsigned char *pip2 = ip_addr(ip2); + + while (nb-- > 0) + { + /* + * Error if overflow on last byte. This test is tricky + * because if the subtraction == 128 and res is negative, or + * if subtraction == -128 and res is positive, the result + * would still fit in 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. */ + if (pip[nb] != pip2[nb]) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("result out of range"))); + } + else + res += (int64)(pip[nb] - pip2[nb]) << (byte * 8); + + byte++; + } + } + + PG_RETURN_INT64(res); +} |