diff --git a/regress/expected/agtype.out b/regress/expected/agtype.out index 0f4775b88..fa6af8dc5 100644 --- a/regress/expected/agtype.out +++ b/regress/expected/agtype.out @@ -601,6 +601,41 @@ SELECT agtype_any_div('-1::numeric', 0); ERROR: division by zero SELECT agtype_any_div('-1.0::numeric', 0); ERROR: division by zero +SELECT agtype_mod('1', '0'); +ERROR: division by zero +SELECT agtype_mod('1', '0.0'); +ERROR: division by zero +SELECT agtype_mod('1.0', '0'); +ERROR: division by zero +SELECT agtype_mod('1.0', '0.0'); +ERROR: division by zero +SELECT agtype_mod('1', '0::numeric'); +ERROR: division by zero +SELECT agtype_mod('1.0', '0::numeric'); +ERROR: division by zero +SELECT agtype_mod('1::numeric', '0'); +ERROR: division by zero +SELECT agtype_mod('1::numeric', '0.0'); +ERROR: division by zero +SELECT agtype_mod('1::numeric', '0::numeric'); +ERROR: division by zero +SELECT agtype_any_mod('1', 0); +ERROR: division by zero +SELECT agtype_any_mod('1.0', 0); +ERROR: division by zero +SELECT agtype_any_mod('-1::numeric', 0); +ERROR: division by zero +SELECT agtype_any_mod('-1.0::numeric', 0); +ERROR: division by zero +-- +-- Should get 0 - INT64_MIN % -1 is well defined +-- +SELECT agtype_mod('-9223372036854775808', '-1'); + agtype_mod +------------ + 0 +(1 row) + -- -- Should get Infinity -- diff --git a/regress/sql/agtype.sql b/regress/sql/agtype.sql index e13edb16d..e351bbafe 100644 --- a/regress/sql/agtype.sql +++ b/regress/sql/agtype.sql @@ -199,6 +199,26 @@ SELECT agtype_any_div('1.0', 0); SELECT agtype_any_div('-1::numeric', 0); SELECT agtype_any_div('-1.0::numeric', 0); +SELECT agtype_mod('1', '0'); +SELECT agtype_mod('1', '0.0'); +SELECT agtype_mod('1.0', '0'); +SELECT agtype_mod('1.0', '0.0'); +SELECT agtype_mod('1', '0::numeric'); +SELECT agtype_mod('1.0', '0::numeric'); +SELECT agtype_mod('1::numeric', '0'); +SELECT agtype_mod('1::numeric', '0.0'); +SELECT agtype_mod('1::numeric', '0::numeric'); + +SELECT agtype_any_mod('1', 0); +SELECT agtype_any_mod('1.0', 0); +SELECT agtype_any_mod('-1::numeric', 0); +SELECT agtype_any_mod('-1.0::numeric', 0); + +-- +-- Should get 0 - INT64_MIN % -1 is well defined +-- +SELECT agtype_mod('-9223372036854775808', '-1'); + -- -- Should get Infinity -- diff --git a/src/backend/utils/adt/agtype_ops.c b/src/backend/utils/adt/agtype_ops.c index f7f45b467..e0d9dd6e1 100644 --- a/src/backend/utils/adt/agtype_ops.c +++ b/src/backend/utils/adt/agtype_ops.c @@ -912,24 +912,64 @@ Datum agtype_mod(PG_FUNCTION_ARGS) if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER) { + if (agtv_rhs->val.int_value == 0) + { + ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + PG_RETURN_NULL(); + } + agtv_result.type = AGTV_INTEGER; - agtv_result.val.int_value = agtv_lhs->val.int_value % - agtv_rhs->val.int_value; + + /* + * INT64_MIN % -1 is zero but overflows the quotient, which traps on + * some platforms. int8mod special cases it the same way. + */ + if (agtv_rhs->val.int_value == -1) + { + agtv_result.val.int_value = 0; + } + else + { + agtv_result.val.int_value = agtv_lhs->val.int_value % + agtv_rhs->val.int_value; + } } else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT) { + if (agtv_rhs->val.float_value == 0) + { + ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + PG_RETURN_NULL(); + } + agtv_result.type = AGTV_FLOAT; agtv_result.val.float_value = fmod(agtv_lhs->val.float_value, agtv_rhs->val.float_value); } else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_INTEGER) { + if (agtv_rhs->val.int_value == 0) + { + ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + PG_RETURN_NULL(); + } + agtv_result.type = AGTV_FLOAT; agtv_result.val.float_value = fmod(agtv_lhs->val.float_value, agtv_rhs->val.int_value); } else if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_FLOAT) { + if (agtv_rhs->val.float_value == 0) + { + ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + PG_RETURN_NULL(); + } + agtv_result.type = AGTV_FLOAT; agtv_result.val.float_value = fmod(agtv_lhs->val.int_value, agtv_rhs->val.float_value);