From 1c9285736b2b3f7b5b1a7aaafa778bcb0a119f1f Mon Sep 17 00:00:00 2001 From: Artur Zakirov Date: Mon, 5 Feb 2024 13:52:01 +0100 Subject: [PATCH] Added new arithmetic operators --- .gitignore | 2 + saturated_int--0.0.1.sql | 77 ++++++++++++++++++++++++++++------ src/saturated_int.c | 89 ++++++++++++++++++++++++++++++++++------ 3 files changed, 144 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 4b05098..0381d27 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ *.o *.so +.deps/ + test/results test/regression.diffs test/regression.out diff --git a/saturated_int--0.0.1.sql b/saturated_int--0.0.1.sql index eca2f13..30f7b6e 100644 --- a/saturated_int--0.0.1.sql +++ b/saturated_int--0.0.1.sql @@ -1,47 +1,50 @@ -- Define saturated_int -CREATE FUNCTION sat_int4_in(cstring) +CREATE FUNCTION saturated_int_in(cstring) RETURNS saturated_int AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; -CREATE FUNCTION sat_int4_out(saturated_int) +CREATE FUNCTION saturated_int_out(saturated_int) RETURNS cstring AS 'int4out' LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; -CREATE FUNCTION sat_int4_recv(internal) +CREATE FUNCTION saturated_int_recv(internal) RETURNS saturated_int AS 'int4recv' LANGUAGE internal IMMUTABLE PARALLEL SAFE; -CREATE FUNCTION sat_int4_send(saturated_int) +CREATE FUNCTION saturated_int_send(saturated_int) RETURNS bytea AS 'int4send' LANGUAGE internal IMMUTABLE PARALLEL SAFE; CREATE TYPE saturated_int ( - input = sat_int4_in, - output = sat_int4_out, - receive = sat_int4_recv, - send = sat_int4_send, + input = saturated_int_in, + output = saturated_int_out, + receive = saturated_int_recv, + send = saturated_int_send, like = integer, category = 'N' ); -- Define casts into saturated_int -CREATE FUNCTION sat_int8to4(bigint) +CREATE FUNCTION saturated_int8to4(bigint) RETURNS saturated_int AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; CREATE CAST (bigint AS saturated_int) -WITH FUNCTION sat_int8to4(bigint) AS ASSIGNMENT; +WITH FUNCTION saturated_int8to4(bigint) AS ASSIGNMENT; CREATE CAST (integer AS saturated_int) WITHOUT FUNCTION AS ASSIGNMENT; +CREATE CAST (saturated_int as integer) +WITHOUT FUNCTION AS ASSIGNMENT; + -- Define comparison operators CREATE FUNCTION saturated_int_eq(saturated_int, saturated_int) @@ -195,17 +198,67 @@ AS -- Define arithmetic operators +CREATE FUNCTION saturated_int_mul(saturated_int, saturated_int) +RETURNS saturated_int +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR * ( + leftarg = saturated_int, + rightarg = saturated_int, + procedure = saturated_int_mul, + commutator = '*' +); +COMMENT ON OPERATOR *(saturated_int, saturated_int) IS 'multiply'; + +CREATE FUNCTION saturated_int_div(saturated_int, saturated_int) +RETURNS saturated_int +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR / ( + leftarg = saturated_int, + rightarg = saturated_int, + procedure = saturated_int_div +); +COMMENT ON OPERATOR /(saturated_int, saturated_int) IS 'divide'; + +CREATE FUNCTION saturated_int_mod(saturated_int, saturated_int) +RETURNS saturated_int +AS 'int4mod' +LANGUAGE internal IMMUTABLE PARALLEL SAFE; + +CREATE OPERATOR % ( + leftarg = saturated_int, + rightarg = saturated_int, + procedure = saturated_int_mod +); +COMMENT ON OPERATOR %(saturated_int, saturated_int) IS 'modulus'; + +CREATE FUNCTION saturated_int_pl(saturated_int, saturated_int) +RETURNS saturated_int +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR + ( + leftarg = saturated_int, + rightarg = saturated_int, + procedure = saturated_int_pl, + commutator = '+' +); +COMMENT ON OPERATOR +(saturated_int, saturated_int) IS 'add'; + -- Define aggregate functions -CREATE FUNCTION sat_int4_sum(state saturated_int, val saturated_int) +CREATE FUNCTION saturated_int_sum(state saturated_int, val saturated_int) RETURNS saturated_int AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE PARALLEL SAFE; CREATE AGGREGATE sum(saturated_int) ( - sfunc = sat_int4_sum, + sfunc = saturated_int_sum, stype = saturated_int, -- combinefunc = ..., -- msfunc = ..., diff --git a/src/saturated_int.c b/src/saturated_int.c index f3a6294..19b3e8f 100644 --- a/src/saturated_int.c +++ b/src/saturated_int.c @@ -1,3 +1,4 @@ +#include "c.h" #include "postgres.h" #include "fmgr.h" @@ -10,9 +11,14 @@ PG_MODULE_MAGIC; -PG_FUNCTION_INFO_V1(sat_int4_in); -PG_FUNCTION_INFO_V1(sat_int8to4); -PG_FUNCTION_INFO_V1(sat_int4_sum); +PG_FUNCTION_INFO_V1(saturated_int_in); +PG_FUNCTION_INFO_V1(saturated_int8to4); + +PG_FUNCTION_INFO_V1(saturated_int_sum); + +PG_FUNCTION_INFO_V1(saturated_int_mul); +PG_FUNCTION_INFO_V1(saturated_int_div); +PG_FUNCTION_INFO_V1(saturated_int_pl); /* * Cast bigint to integer with saturation. @@ -21,7 +27,7 @@ PG_FUNCTION_INFO_V1(sat_int4_sum); * Return INT_MAX if the parsed value greater than INT_MAX. */ static inline int32 -sat_int8to4_impl(int64 num) +saturated_int8to4_impl(int64 num) { if (unlikely(num < INT_MIN)) return INT_MIN; @@ -35,16 +41,16 @@ sat_int8to4_impl(int64 num) * Parse integer with saturation. */ Datum -sat_int4_in(PG_FUNCTION_ARGS) +saturated_int_in(PG_FUNCTION_ARGS) { char *arg = PG_GETARG_CSTRING(0); #if PG_VERSION_NUM < 150000 int64 result; (void) scanint8(arg, false, &result); - PG_RETURN_INT32(sat_int8to4_impl(result)); + PG_RETURN_INT32(saturated_int8to4_impl(result)); #else - PG_RETURN_INT32(sat_int8to4_impl(pg_strtoint64(arg))); + PG_RETURN_INT32(saturated_int8to4_impl(pg_strtoint64(arg))); #endif } @@ -52,18 +58,18 @@ sat_int4_in(PG_FUNCTION_ARGS) * Cast bigint to integer with saturation. */ Datum -sat_int8to4(PG_FUNCTION_ARGS) +saturated_int8to4(PG_FUNCTION_ARGS) { int64 arg = PG_GETARG_INT64(0); - PG_RETURN_INT32(sat_int8to4_impl(arg)); + PG_RETURN_INT32(saturated_int8to4_impl(arg)); } /* * Compute the saturating addition (https://en.wikipedia.org/wiki/Saturation_arithmetic). */ Datum -sat_int4_sum(PG_FUNCTION_ARGS) +saturated_int_sum(PG_FUNCTION_ARGS) { int32 oldsum = PG_GETARG_INT32(0); int32 newval; @@ -87,11 +93,70 @@ sat_int4_sum(PG_FUNCTION_ARGS) * If oldsum < 0 there can be only overflow if newval < INT_MIN - oldsum. */ newval = PG_GETARG_INT32(1); - if (oldsum >= 0 && newval > INT_MAX - oldsum) + if (unlikely(oldsum >= 0 && newval > INT_MAX - oldsum)) PG_RETURN_INT32(INT_MAX); - else if (oldsum < 0 && newval < INT_MIN - oldsum) + else if (unlikely(oldsum < 0 && newval < INT_MIN - oldsum)) PG_RETURN_INT32(INT_MIN); /* It is safe to return sum */ PG_RETURN_INT32(oldsum + newval); } + +/* + * Arithmetic functions for operators. + */ + +Datum +saturated_int_mul(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + + PG_RETURN_INT32(saturated_int8to4_impl(arg1 * arg2)); +} + +/* + * Copy of int4div() with changes for saturated_int. + */ +Datum +saturated_int_div(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + int32 result; + + if (unlikely(arg2 == 0)) + { + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* + * INT_MIN / -1 will return INT_MAX, which isn't exactly just negation. + */ + if (arg2 == -1) + { + if (unlikely(arg1 == PG_INT32_MIN)) + PG_RETURN_INT32(PG_INT32_MAX); + result = -arg1; + PG_RETURN_INT32(result); + } + + /* No overflow is possible */ + + result = arg1 / arg2; + + PG_RETURN_INT32(result); +} + +Datum +saturated_int_pl(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + + PG_RETURN_INT32(saturated_int8to4_impl(arg1 + arg2)); +}