Hi Haibo,

Thanks for reviewing.

I agree that the SQL-callable pg_catalog.interval(interval, int4) path is the
important case to cover here.  The v1 test was already exercising that direct
call, but it used a DO block intentionally so that the regression test checks
the SQLSTATE is invalid_parameter_value, rather than only matching the error
message text.

In v2, I kept that structure and updated the test comment to make this clearer.

Regards,
Feng
From 1d0627c38f729a8f9059b6894206eef96bdea882 Mon Sep 17 00:00:00 2001
From: Feng Wu <[email protected]>
Date: Tue, 30 Jun 2026 10:56:31 +0800
Subject: [PATCH v2] Avoid internal error for invalid interval typmods

The interval length-coercion function can be called directly from SQL with an arbitrary typmod value.  When the typmod encodes an unrecognized interval range, AdjustIntervalForTypmod() currently reports the condition with elog(ERROR), producing SQLSTATE XX000 for user input.

Report the condition as ERRCODE_INVALID_PARAMETER_VALUE instead, matching the nearby precision validation and allowing callers to handle the error as an ordinary invalid parameter value.

Add a regression test that calls pg_catalog.interval(interval, int4) directly.  It uses a DO block so the test verifies the SQLSTATE is invalid_parameter_value, rather than only matching the emitted error text.

Signed-off-by: Feng Wu <[email protected]>
---
 src/backend/utils/adt/timestamp.c      |  4 +++-
 src/test/regress/expected/interval.out | 10 ++++++++++
 src/test/regress/sql/interval.sql      |  9 +++++++++
 3 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index a20e7ea1..01b9ed52 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -1489,7 +1489,9 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod,
 			/* fractional-second rounding will be dealt with below */
 		}
 		else
-			elog(ERROR, "unrecognized interval typmod: %d", typmod);
+			ereturn(escontext, false,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unrecognized interval typmod: %d", typmod)));
 
 		/* Need to adjust sub-second precision? */
 		if (precision != INTERVAL_FULL_PRECISION)
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index a16e3ccd..dae01bb9 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -857,6 +857,16 @@ SELECT interval(2) '1 day 01:23:45.6789';
  1 day 01:23:45.68
 (1 row)
 
+-- invalid typmods passed to the SQL-callable length-coercion function are
+-- user errors, not internal errors.  Use a DO block to verify the SQLSTATE.
+DO $$
+BEGIN
+  PERFORM pg_catalog.interval(interval '1 day', 1539);
+EXCEPTION WHEN invalid_parameter_value THEN
+  RAISE NOTICE 'invalid interval typmod rejected';
+END
+$$;
+NOTICE:  invalid interval typmod rejected
 SELECT interval '12:34.5678' minute to second(2);  -- per SQL spec
   interval   
 -------------
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index 43bc7939..5edc822e 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -257,6 +257,15 @@ SELECT interval '123 2:03 -2:04'; -- not ok, redundant hh:mm fields
 -- test syntaxes for restricted precision
 SELECT interval(0) '1 day 01:23:45.6789';
 SELECT interval(2) '1 day 01:23:45.6789';
+-- invalid typmods passed to the SQL-callable length-coercion function are
+-- user errors, not internal errors.  Use a DO block to verify the SQLSTATE.
+DO $$
+BEGIN
+  PERFORM pg_catalog.interval(interval '1 day', 1539);
+EXCEPTION WHEN invalid_parameter_value THEN
+  RAISE NOTICE 'invalid interval typmod rejected';
+END
+$$;
 SELECT interval '12:34.5678' minute to second(2);  -- per SQL spec
 SELECT interval '1.234' second;
 SELECT interval '1.234' second(2);
-- 
2.53.0

Reply via email to