[PATCH] Avoid internal error for invalid interval typmods
Hi,
While fuzzing PostgreSQL, I noticed that the interval length-coercion
function can report an internal error for user-supplied input:
SELECT pg_catalog.interval(interval '1 day', 1539);
Currently this fails with:
ERROR: unrecognized interval typmod: 1539
The error is raised with elog(ERROR) in AdjustIntervalForTypmod(), so it
is reported as SQLSTATE XX000. Since pg_catalog.interval(interval, int4)
is callable from SQL, arbitrary typmod values can reach this path.
The patch below changes that case to report
ERRCODE_INVALID_PARAMETER_VALUE instead, matching the nearby precision
validation in the same function. It also adds a regression test that
catches the error as invalid_parameter_value.
Tested with the interval regression test.
Regards,
Feng
---
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..c5e10482 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 length-coercion function are user errors,
+-- not internal errors
+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..88794c21 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 length-coercion function are user errors,
+-- not internal errors
+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.50.1 (Apple Git-155)
Apologies for the duplicate email. I sent the patch twice by mistake
from two different clients. The two copies are identical, so either one
can be used.
Regards,
Feng
On Mon, Jun 29, 2026 at 9:26 PM Feng Wu <wufengwufengwufeng@gmail.com> wrote:
Hi,
While fuzzing PostgreSQL, I noticed that the interval length-coercion
function can report an internal error for user-supplied input:SELECT pg_catalog.interval(interval '1 day', 1539);
Currently this fails with:
ERROR: unrecognized interval typmod: 1539
The error is raised with elog(ERROR) in AdjustIntervalForTypmod(), so it
is reported as SQLSTATE XX000. Since pg_catalog.interval(interval, int4)
is callable from SQL, arbitrary typmod values can reach this path.The patch below changes that case to report
ERRCODE_INVALID_PARAMETER_VALUE instead, matching the nearby precision
validation in the same function. It also adds a regression test that
catches the error as invalid_parameter_value.
I wonder whether AdjustIntervalForTypmod() should be changed too.
pg_catalog.interval(interval, int4) is SQL-callable, so users can pass
arbitrary typmod values directly. If such an invalid typmod reaches
AdjustIntervalForTypmod(), reporting XX000 seems inappropriate, since
it is not really an internal error but invalid user input.
Tested with the interval regression test.
Could we use ERRCODE_INVALID_PARAMETER_VALUE for this case and add a
regression test covering a direct call to
pg_catalog.interval(interval, int4) with an invalid typmod?
Regards,
Feng---
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..c5e10482 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 length-coercion function are user errors, +-- not internal errors +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..88794c21 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 length-coercion function are user errors, +-- not internal errors +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.50.1 (Apple Git-155)
Regards
Haibo
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
Attachments:
v2-0001-Avoid-internal-error-for-invalid-interval-typmods.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Avoid-internal-error-for-invalid-interval-typmods.patchDownload+22-2
On Tue, Jun 30, 2026 at 6:49 PM Feng Wu <wufengwufengwufeng@gmail.com> wrote:
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
Thank you for fixing this. v2 looks good to me.
Regards
Haibo
Thanks.
I tried to add this patch to the CommitFest, but my community account is still
within the cool-off period for commitfest.postgresql.org login. If someone is
able to add it meanwhile, I would appreciate it.
Regards,
Feng
Haibo Yan reviewed v2 on the mailing list and said it looks good to him.
Haibo Yan reviewed v2 on the mailing list and said it looks good to him.
Hi Feng,
I tested the v2 patch on PostgreSQL 20devel.
The patch applied cleanly, and I rebuilt and restarted the server successfully.
Before applying the patch, calling:
SELECT pg_catalog.interval(interval '1 day', 1539);
reported:
ERROR: unrecognized interval typmod: 1539
After applying the patch, the same DO block produced:
NOTICE: invalid interval typmod rejected
which confirms that the error is now reported as
invalid_parameter_value instead of an internal error.
I also verified that the existing interval behavior remains unchanged:
SELECT interval(0) '1 day 01:23:45.6789'; returned 1 day 01:23:46
SELECT interval(2) '1 day 01:23:45.6789'; returned 1 day 01:23:45.68
The patch worked as expected in my testing.
Regards,
solai
Hi solai,
Thanks for testing the v2 patch and for moving it to Ready for Committer.
I also noticed the current CFBot failure in the Windows MinGW job, but
it appears to be an unrelated version mismatch with utf8_and_win.dll.
I will keep an eye on the next CFBot run.
Regards,
Feng