From 2a3de9add95a6d0635f70a93509a156c1a08a806 Mon Sep 17 00:00:00 2001 From: "Chao Li (Evan)" Date: Wed, 14 Oct 2026 18:40:56 +0800 Subject: [PATCH v1] Fix numeric log() with an infinite argument and a base at or below one. Return negative infinity when the base is between zero and one, and raise division by zero when the base is one, matching the finite-input behavior. Previously, both cases incorrectly returned positive infinity. Add regression tests for both cases. Author: Chao Li Reviewed-by: Discussion: https://postgr.es/m/ Backpatch-through: 14 --- src/backend/utils/adt/numeric.c | 21 ++++++++++++++++----- src/test/regress/expected/numeric.out | 8 ++++++++ src/test/regress/sql/numeric.sql | 2 ++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index f4dfec56659..add1406c518 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -3869,13 +3869,13 @@ numeric_log(PG_FUNCTION_ARGS) sign2 = numeric_sign_internal(num2); if (sign1 < 0 || sign2 < 0) ereport(ERROR, - (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG), - errmsg("cannot take logarithm of a negative number"))); + errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG), + errmsg("cannot take logarithm of a negative number")); /* fail on zero inputs, as log_var would */ if (sign1 == 0 || sign2 == 0) ereport(ERROR, - (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG), - errmsg("cannot take logarithm of zero"))); + errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG), + errmsg("cannot take logarithm of zero")); if (NUMERIC_IS_PINF(num1)) { /* log(Inf, Inf) reduces to Inf/Inf, so it's NaN */ @@ -3885,7 +3885,18 @@ numeric_log(PG_FUNCTION_ARGS) PG_RETURN_NUMERIC(make_result(&const_zero)); } Assert(NUMERIC_IS_PINF(num2)); - /* log(finite-positive, Inf) is Inf */ + + /* + * log(base, Inf) = Inf / ln(base), so the sign depends on whether + * base is above or below 1. If base = 1, fail as log_var() would. + */ init_var_from_num(num1, &arg1); + sign1 = cmp_var(&arg1, &const_one); + if (sign1 == 0) + ereport(ERROR, + errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero")); + if (sign1 < 0) + PG_RETURN_NUMERIC(make_result(&const_ninf)); PG_RETURN_NUMERIC(make_result(&const_pinf)); } diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out index c58e232a263..cec2bc44b4c 100644 --- a/src/test/regress/expected/numeric.out +++ b/src/test/regress/expected/numeric.out @@ -933,6 +933,14 @@ FROM v AS v1(x1), v AS v2(x2); NaN | NaN | NaN (16 rows) +SELECT log('0.5'::numeric, 'inf'); + log +----------- + -Infinity +(1 row) + +SELECT log('1'::numeric, 'inf'); +ERROR: division by zero SELECT log('0'::numeric, '10'); ERROR: cannot take logarithm of zero SELECT log('10'::numeric, '0'); diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql index 640c6d92f4c..5fd5c02f636 100644 --- a/src/test/regress/sql/numeric.sql +++ b/src/test/regress/sql/numeric.sql @@ -713,6 +713,8 @@ SELECT x1, x2, log(x1, x2) FROM v AS v1(x1), v AS v2(x2); +SELECT log('0.5'::numeric, 'inf'); +SELECT log('1'::numeric, 'inf'); SELECT log('0'::numeric, '10'); SELECT log('10'::numeric, '0'); SELECT log('-inf'::numeric, '10'); -- 2.50.1 (Apple Git-155)