date_trunc invalid units with infinite value
Hi all,
When looking at the date_trunc function, I noticed that it wasn't
rejecting invalid units if the value was infinite. It's slightly
annoying to fix this, but we already do something very similar for
date_part. I have attached a patch that would fix this issue, let me
know if you think it's worth pursuing.
Thanks,
Joseph Koshakow
Attachments:
v1-0001-Fix-date_trunc-units-for-infinite-intervals.patchtext/x-patch; charset=US-ASCII; name=v1-0001-Fix-date_trunc-units-for-infinite-intervals.patchDownload
From c046465d4532e9b9086e0aadd13526e2a284b072 Mon Sep 17 00:00:00 2001
From: Joseph Koshakow <koshy44@gmail.com>
Date: Sun, 1 Dec 2024 14:55:35 -0500
Subject: [PATCH v1] Fix date_trunc units for infinite intervals
Previously, if an infinite value was passed to date_trunc, then the
same infinite value would always be returned regardless of the unit.
This commit updates the function so that an error is returned when an
invalid unit is passed to date_trunc with an infinite value.
This matches the behavior of date_trunc with a finite value and
date_part with an infinite value.
---
src/backend/utils/adt/timestamp.c | 121 ++++++++++++++++++----
src/test/regress/expected/interval.out | 9 ++
src/test/regress/expected/timestamp.out | 2 +
src/test/regress/expected/timestamptz.out | 4 +
src/test/regress/sql/interval.sql | 8 ++
src/test/regress/sql/timestamp.sql | 2 +
src/test/regress/sql/timestamptz.sql | 4 +
7 files changed, 131 insertions(+), 19 deletions(-)
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index e00cd3c969..a2489caa6b 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -4620,9 +4620,6 @@ timestamp_trunc(PG_FUNCTION_ARGS)
struct pg_tm tt,
*tm = &tt;
- if (TIMESTAMP_NOT_FINITE(timestamp))
- PG_RETURN_TIMESTAMP(timestamp);
-
lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
VARSIZE_ANY_EXHDR(units),
false);
@@ -4631,6 +4628,39 @@ timestamp_trunc(PG_FUNCTION_ARGS)
if (type == UNITS)
{
+ if (TIMESTAMP_NOT_FINITE(timestamp))
+ {
+ /*
+ * Errors thrown here for invalid units should exactly match those
+ * below, else there will be unexpected discrepancies between
+ * finite- and infinite-input cases.
+ */
+ switch (val)
+ {
+ case DTK_WEEK:
+ case DTK_MILLENNIUM:
+ case DTK_CENTURY:
+ case DTK_DECADE:
+ case DTK_YEAR:
+ case DTK_QUARTER:
+ case DTK_MONTH:
+ case DTK_DAY:
+ case DTK_HOUR:
+ case DTK_MINUTE:
+ case DTK_SECOND:
+ case DTK_MILLISEC:
+ case DTK_MICROSEC:
+ PG_RETURN_TIMESTAMP(timestamp);
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unit \"%s\" not supported for type %s",
+ lowunits, format_type_be(TIMESTAMPOID))));
+ result = 0;
+ }
+ }
+
if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
@@ -4836,6 +4866,40 @@ timestamptz_trunc_internal(text *units, TimestampTz timestamp, pg_tz *tzp)
if (type == UNITS)
{
+ if (TIMESTAMP_NOT_FINITE(timestamp))
+ {
+ /*
+ * Errors thrown here for invalid units should exactly match those
+ * below, else there will be unexpected discrepancies between
+ * finite- and infinite-input cases.
+ */
+ switch (val)
+ {
+ case DTK_WEEK:
+ case DTK_MILLENNIUM:
+ case DTK_CENTURY:
+ case DTK_DECADE:
+ case DTK_YEAR:
+ case DTK_QUARTER:
+ case DTK_MONTH:
+ case DTK_DAY:
+ case DTK_HOUR:
+ case DTK_MINUTE:
+ case DTK_SECOND:
+ case DTK_MILLISEC:
+ case DTK_MICROSEC:
+ PG_RETURN_TIMESTAMPTZ(timestamp);
+ break;
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unit \"%s\" not supported for type %s",
+ lowunits, format_type_be(TIMESTAMPTZOID))));
+ result = 0;
+ }
+ }
+
if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, tzp) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
@@ -4966,9 +5030,6 @@ timestamptz_trunc(PG_FUNCTION_ARGS)
TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz result;
- if (TIMESTAMP_NOT_FINITE(timestamp))
- PG_RETURN_TIMESTAMPTZ(timestamp);
-
result = timestamptz_trunc_internal(units, timestamp, session_timezone);
PG_RETURN_TIMESTAMPTZ(result);
@@ -4986,13 +5047,6 @@ timestamptz_trunc_zone(PG_FUNCTION_ARGS)
TimestampTz result;
pg_tz *tzp;
- /*
- * timestamptz_zone() doesn't look up the zone for infinite inputs, so we
- * don't do so here either.
- */
- if (TIMESTAMP_NOT_FINITE(timestamp))
- PG_RETURN_TIMESTAMP(timestamp);
-
/*
* Look up the requested timezone.
*/
@@ -5020,12 +5074,6 @@ interval_trunc(PG_FUNCTION_ARGS)
result = (Interval *) palloc(sizeof(Interval));
- if (INTERVAL_NOT_FINITE(interval))
- {
- memcpy(result, interval, sizeof(Interval));
- PG_RETURN_INTERVAL_P(result);
- }
-
lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
VARSIZE_ANY_EXHDR(units),
false);
@@ -5034,6 +5082,41 @@ interval_trunc(PG_FUNCTION_ARGS)
if (type == UNITS)
{
+ if (INTERVAL_NOT_FINITE(interval))
+ {
+ /*
+ * Errors thrown here for invalid units should exactly match those
+ * below, else there will be unexpected discrepancies between
+ * finite- and infinite-input cases.
+ */
+ switch (val)
+ {
+ case DTK_MILLENNIUM:
+ case DTK_CENTURY:
+ case DTK_DECADE:
+ case DTK_YEAR:
+ case DTK_QUARTER:
+ case DTK_MONTH:
+ case DTK_DAY:
+ case DTK_HOUR:
+ case DTK_MINUTE:
+ case DTK_SECOND:
+ case DTK_MILLISEC:
+ case DTK_MICROSEC:
+ memcpy(result, interval, sizeof(Interval));
+ PG_RETURN_INTERVAL_P(result);
+ break;
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unit \"%s\" not supported for type %s",
+ lowunits, format_type_be(INTERVALOID)),
+ (val == DTK_WEEK) ? errdetail("Months usually have fractional weeks.") : 0));
+ result = 0;
+ }
+ }
+
interval2itm(*interval, tm);
switch (val)
{
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index e5d919d0cf..a16e3ccdb2 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -2219,6 +2219,15 @@ SELECT i AS interval, date_trunc('hour', i)
-infinity | -infinity
(2 rows)
+SELECT i AS interval, date_trunc('week', i)
+ FROM INFINITE_INTERVAL_TBL
+ WHERE NOT isfinite(i);
+ERROR: unit "week" not supported for type interval
+DETAIL: Months usually have fractional weeks.
+SELECT i AS interval, date_trunc('ago', i)
+ FROM INFINITE_INTERVAL_TBL
+ WHERE NOT isfinite(i);
+ERROR: unit "ago" not recognized for type interval
SELECT i AS interval, justify_days(i), justify_hours(i), justify_interval(i)
FROM INFINITE_INTERVAL_TBL
WHERE NOT isfinite(i);
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index e287260051..6aaa19c8f4 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -591,6 +591,8 @@ SELECT date_trunc( 'week', timestamp '2004-02-29 15:44:17.71393' ) AS week_trunc
Mon Feb 23 00:00:00 2004
(1 row)
+SELECT date_trunc( 'ago', timestamp 'infinity' ) AS invalid_trunc;
+ERROR: unit "ago" not recognized for type timestamp without time zone
-- verify date_bin behaves the same as date_trunc for relevant intervals
-- case 1: AD dates, origin < input
SELECT
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index b437613ac8..a6dd45626c 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -701,6 +701,8 @@ SELECT date_trunc( 'week', timestamp with time zone '2004-02-29 15:44:17.71393'
Mon Feb 23 00:00:00 2004 PST
(1 row)
+SELECT date_trunc( 'ago', timestamp with time zone 'infinity' ) AS invalid_trunc;
+ERROR: unit "ago" not recognized for type timestamp with time zone
SELECT date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'Australia/Sydney') as sydney_trunc; -- zone name
sydney_trunc
------------------------------
@@ -719,6 +721,8 @@ SELECT date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'VET
Thu Feb 15 20:00:00 2001 PST
(1 row)
+SELECT date_trunc('ago', timestamp with time zone 'infinity', 'GMT') AS invalid_zone_trunc;
+ERROR: unit "ago" not recognized for type timestamp with time zone
-- verify date_bin behaves the same as date_trunc for relevant intervals
SELECT
str,
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index 55054ae65d..43bc793925 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -776,6 +776,14 @@ SELECT i AS interval, date_trunc('hour', i)
FROM INFINITE_INTERVAL_TBL
WHERE NOT isfinite(i);
+SELECT i AS interval, date_trunc('week', i)
+ FROM INFINITE_INTERVAL_TBL
+ WHERE NOT isfinite(i);
+
+SELECT i AS interval, date_trunc('ago', i)
+ FROM INFINITE_INTERVAL_TBL
+ WHERE NOT isfinite(i);
+
SELECT i AS interval, justify_days(i), justify_hours(i), justify_interval(i)
FROM INFINITE_INTERVAL_TBL
WHERE NOT isfinite(i);
diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql
index 748469576d..55f80530ea 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -176,6 +176,8 @@ SELECT d1 - timestamp without time zone '1997-01-02' AS diff
SELECT date_trunc( 'week', timestamp '2004-02-29 15:44:17.71393' ) AS week_trunc;
+SELECT date_trunc( 'ago', timestamp 'infinity' ) AS invalid_trunc;
+
-- verify date_bin behaves the same as date_trunc for relevant intervals
-- case 1: AD dates, origin < input
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index 6b91e7eddc..a92586c363 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -200,10 +200,14 @@ SELECT d1 - timestamp with time zone '1997-01-02' AS diff
FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01';
SELECT date_trunc( 'week', timestamp with time zone '2004-02-29 15:44:17.71393' ) AS week_trunc;
+SELECT date_trunc( 'ago', timestamp with time zone 'infinity' ) AS invalid_trunc;
SELECT date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'Australia/Sydney') as sydney_trunc; -- zone name
SELECT date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'GMT') as gmt_trunc; -- fixed-offset abbreviation
SELECT date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'VET') as vet_trunc; -- variable-offset abbreviation
+SELECT date_trunc('ago', timestamp with time zone 'infinity', 'GMT') AS invalid_zone_trunc;
+
+
-- verify date_bin behaves the same as date_trunc for relevant intervals
SELECT
--
2.43.0
On Sun, Dec 01, 2024 at 03:09:17PM -0500, Joseph Koshakow wrote:
When looking at the date_trunc function, I noticed that it wasn't
rejecting invalid units if the value was infinite. It's slightly
annoying to fix this, but we already do something very similar for
date_part. I have attached a patch that would fix this issue, let me
know if you think it's worth pursuing.
I agree that it is inconsistent that we allow infinite values to go
through this function call even for fields that are not listed as
supported by the documentation. So, yes, I think that what you are
doing the right thing by applying the check based on the units
supported, but I also doubt that it is something that we could
backpatch as it would cause queries to work now to suddenly break.
Thoughts and comments from others are welcome.
--
Michael
On Thu, Dec 05, 2024 at 02:34:41PM +0900, Michael Paquier wrote:
I agree that it is inconsistent that we allow infinite values to go
through this function call even for fields that are not listed as
supported by the documentation. So, yes, I think that what you are
doing the right thing by applying the check based on the units
supported, but I also doubt that it is something that we could
backpatch as it would cause queries to work now to suddenly break.Thoughts and comments from others are welcome.
Hearing nothing, I have looked at this patch again and I think that
I'm OK with your proposal. While the discrepancy is annoying for
back-branches, this causes a slight change of behavior, so I have no
backpatch in mind.
I am planning to get this one applied around the end of this week on
Friday for HEAD, that should be enough if there are comments and/or
objections.
--
Michael
On Tue, Dec 24, 2024 at 04:33:47PM +0900, Michael Paquier wrote:
I am planning to get this one applied around the end of this week on
Friday for HEAD, that should be enough if there are comments and/or
objections.
And done for now. If there are any remarks and/or objections, of
course feel free.
--
Michael
On 27.12.24 05:42, Michael Paquier wrote:
On Tue, Dec 24, 2024 at 04:33:47PM +0900, Michael Paquier wrote:
I am planning to get this one applied around the end of this week on
Friday for HEAD, that should be enough if there are comments and/or
objections.And done for now. If there are any remarks and/or objections, of
course feel free.
It turned out this had a bug, and also the newly added test cases didn't
actually cover the new code, otherwise this would have shown up.
Please review the attached patches with additional test cases and the fix.
See also [0]/messages/by-id/8246d7ff-f4b7-4363-913e-827dadfeb145@eisentraut.org for further context:
[0]: /messages/by-id/8246d7ff-f4b7-4363-913e-827dadfeb145@eisentraut.org
/messages/by-id/8246d7ff-f4b7-4363-913e-827dadfeb145@eisentraut.org
Attachments:
0001-Improve-test-coverage-of-date_trunc-on-infinity.patchtext/plain; charset=UTF-8; name=0001-Improve-test-coverage-of-date_trunc-on-infinity.patchDownload
From 11eb6931c0903ebac40efd95cda201f341bec59b Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Wed, 6 Aug 2025 12:20:44 +0200
Subject: [PATCH 1/2] Improve test coverage of date_trunc() on infinity
Commit d85ce012f99 added new error handling code to date_trunc() of
timestamp, timestamptz, and interval with infinite values. But the
new test cases added by that commit did not actually test the new
code, since they used the "not recognized" unit 'ago' but not a "not
supported" unit, which is what the new code was dealing with. This
patch adds some test cases to cover the new code. I'm using
'timezone' as the not supported unit.
---
src/test/regress/expected/timestamp.out | 10 ++++++++++
src/test/regress/expected/timestamptz.out | 12 ++++++++++++
src/test/regress/sql/timestamp.sql | 4 +++-
src/test/regress/sql/timestamptz.sql | 6 ++++--
4 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index 6aaa19c8f4e..14a9f5b56a6 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -591,6 +591,16 @@ SELECT date_trunc( 'week', timestamp '2004-02-29 15:44:17.71393' ) AS week_trunc
Mon Feb 23 00:00:00 2004
(1 row)
+SELECT date_trunc( 'week', timestamp 'infinity' ) AS inf_trunc;
+ inf_trunc
+-----------
+ infinity
+(1 row)
+
+SELECT date_trunc( 'timezone', timestamp '2004-02-29 15:44:17.71393' ) AS notsupp_trunc;
+ERROR: unit "timezone" not supported for type timestamp without time zone
+SELECT date_trunc( 'timezone', timestamp 'infinity' ) AS notsupp_inf_trunc;
+ERROR: unit "timezone" not supported for type timestamp without time zone
SELECT date_trunc( 'ago', timestamp 'infinity' ) AS invalid_trunc;
ERROR: unit "ago" not recognized for type timestamp without time zone
-- verify date_bin behaves the same as date_trunc for relevant intervals
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index 2a69953ff25..953656affa4 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -760,6 +760,16 @@ SELECT date_trunc( 'week', timestamp with time zone '2004-02-29 15:44:17.71393'
Mon Feb 23 00:00:00 2004 PST
(1 row)
+SELECT date_trunc( 'week', timestamp with time zone 'infinity' ) AS inf_trunc;
+ inf_trunc
+-----------
+ infinity
+(1 row)
+
+SELECT date_trunc( 'timezone', timestamp with time zone '2004-02-29 15:44:17.71393' ) AS notsupp_trunc;
+ERROR: unit "timezone" not supported for type timestamp with time zone
+SELECT date_trunc( 'timezone', timestamp with time zone 'infinity' ) AS notsupp_inf_trunc;
+ERROR: unit "timezone" not supported for type timestamp with time zone
SELECT date_trunc( 'ago', timestamp with time zone 'infinity' ) AS invalid_trunc;
ERROR: unit "ago" not recognized for type timestamp with time zone
SELECT date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'Australia/Sydney') as sydney_trunc; -- zone name
@@ -780,6 +790,8 @@ SELECT date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'VET
Thu Feb 15 20:00:00 2001 PST
(1 row)
+SELECT date_trunc('timezone', timestamp with time zone 'infinity', 'GMT') AS notsupp_zone_trunc;
+ERROR: unit "timezone" not supported for type timestamp with time zone
SELECT date_trunc('ago', timestamp with time zone 'infinity', 'GMT') AS invalid_zone_trunc;
ERROR: unit "ago" not recognized for type timestamp with time zone
-- verify date_bin behaves the same as date_trunc for relevant intervals
diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql
index 55f80530ea0..313757ed041 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -175,7 +175,9 @@ CREATE TABLE TIMESTAMP_TBL (d1 timestamp(2) without time zone);
FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01';
SELECT date_trunc( 'week', timestamp '2004-02-29 15:44:17.71393' ) AS week_trunc;
-
+SELECT date_trunc( 'week', timestamp 'infinity' ) AS inf_trunc;
+SELECT date_trunc( 'timezone', timestamp '2004-02-29 15:44:17.71393' ) AS notsupp_trunc;
+SELECT date_trunc( 'timezone', timestamp 'infinity' ) AS notsupp_inf_trunc;
SELECT date_trunc( 'ago', timestamp 'infinity' ) AS invalid_trunc;
-- verify date_bin behaves the same as date_trunc for relevant intervals
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index caca3123f13..e01618907e0 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -217,15 +217,17 @@ CREATE TABLE TIMESTAMPTZ_TBL (d1 timestamp(2) with time zone);
FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01';
SELECT date_trunc( 'week', timestamp with time zone '2004-02-29 15:44:17.71393' ) AS week_trunc;
+SELECT date_trunc( 'week', timestamp with time zone 'infinity' ) AS inf_trunc;
+SELECT date_trunc( 'timezone', timestamp with time zone '2004-02-29 15:44:17.71393' ) AS notsupp_trunc;
+SELECT date_trunc( 'timezone', timestamp with time zone 'infinity' ) AS notsupp_inf_trunc;
SELECT date_trunc( 'ago', timestamp with time zone 'infinity' ) AS invalid_trunc;
SELECT date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'Australia/Sydney') as sydney_trunc; -- zone name
SELECT date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'GMT') as gmt_trunc; -- fixed-offset abbreviation
SELECT date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'VET') as vet_trunc; -- variable-offset abbreviation
+SELECT date_trunc('timezone', timestamp with time zone 'infinity', 'GMT') AS notsupp_zone_trunc;
SELECT date_trunc('ago', timestamp with time zone 'infinity', 'GMT') AS invalid_zone_trunc;
-
-
-- verify date_bin behaves the same as date_trunc for relevant intervals
SELECT
str,
--
2.50.1
0002-Fix-incorrect-Datum-conversion-in-timestamptz_trunc_.patchtext/plain; charset=UTF-8; name=0002-Fix-incorrect-Datum-conversion-in-timestamptz_trunc_.patchDownload
From 4f9e5dde59efc00c682f14a3047fd432c87f4209 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Wed, 6 Aug 2025 12:37:41 +0200
Subject: [PATCH 2/2] Fix incorrect Datum conversion in
timestamptz_trunc_internal()
It used PG_RETURN_TIMESTAMPTZ(), but the return type is TimestampTz,
not Datum. On 64-bit systems, there is no effect, since this just
ends up casting 64-bit integers back and forth. But on 32-bit
systems, timestamptz is pass-by-reference, and so
PG_RETURN_TIMESTAMPTZ() allocates new memory and returns the address,
but the caller will try to interpret this as a timestamp value. The
effect is that date_trunc(..., 'infinity'::timestamptz) will return
random values (instead of the correct return value 'infinity').
The fix is to use a straight "return" call.
Commit d85ce012f99 added this code but provided tests that did not
cover this correctly. This was fixed in the previous patch.
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://www.postgresql.org/message-id/flat/8246d7ff-f4b7-4363-913e-827dadfeb145%40eisentraut.org
---
src/backend/utils/adt/timestamp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 25cff56c3d0..e640b48205b 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -4954,7 +4954,7 @@ timestamptz_trunc_internal(text *units, TimestampTz timestamp, pg_tz *tzp)
case DTK_SECOND:
case DTK_MILLISEC:
case DTK_MICROSEC:
- PG_RETURN_TIMESTAMPTZ(timestamp);
+ return timestamp;
break;
default:
--
2.50.1
On Wed, Aug 06, 2025 at 01:07:25PM +0200, Peter Eisentraut wrote:
It turned out this had a bug, and also the newly added test cases didn't
actually cover the new code, otherwise this would have shown up.Please review the attached patches with additional test cases and the fix.
See also [0] for further context:
[0]: /messages/by-id/8246d7ff-f4b7-4363-913e-827dadfeb145@eisentraut.org
Yes, confirmed the broken case on 32-bit builds with the incorrect
result returned by timestamptz_trunc_internal():
SELECT date_trunc( 'week', timestamp with time zone 'infinity' );
And confirmed that we don't have any coverage for two code paths as of
HEAD:
- "not supported" in timestamp_trunc() for the entire new section of
where TIMESTAMP_NOT_FINITE() is satisfied.
- "not supported" in timestamptz_trunc_internal() for the entire
section where TIMESTAMP_NOT_FINITE() is satisfied.
0001 adds three new tests for timestamp:
- TIMESTAMP_NOT_FINITE + a valid unit, new path.
- TIMESTAMP_NOT_FINITE + "not supported" unit, new error path
- !TIMESTAMP_NOT_FINITE + "not supported" unit, old error path
0001 four new tests for timestamptz:
1) Three tests for timestamptz_trunc():
- TIMESTAMP_NOT_FINITE + a valid unit, new path.
- TIMESTAMP_NOT_FINITE + "not supported" unit, new path.
- !TIMESTAMP_NOT_FINITE +
2) One test for timestamptz_trunc_zone():
- !TIMESTAMP_NOT_FINITE + "not supported" unit
With what I am reading in your patch, what you are suggesting to add,
and a double-check at the interval tests, that seems complete to me.
This is a v18 open item, for something that I am an owner of as the
committer of d85ce012f99f, so I'll go take care of it. Thanks!
--
Michael
On Thu, Aug 07, 2025 at 09:06:25AM +0900, Michael Paquier wrote:
Yes, confirmed the broken case on 32-bit builds with the incorrect
result returned by timestamptz_trunc_internal():
SELECT date_trunc( 'week', timestamp with time zone 'infinity' );0001 four new tests for timestamptz:
1) Three tests for timestamptz_trunc():
- TIMESTAMP_NOT_FINITE + a valid unit, new path.
- TIMESTAMP_NOT_FINITE + "not supported" unit, new path.
- !TIMESTAMP_NOT_FINITE +
Blurp here. I meant !TIMESTAMP_NOT_FINITE with unsupported unit, old
code path.
2) One test for timestamptz_trunc_zone():
- !TIMESTAMP_NOT_FINITE + "not supported" unit
There can be an extra test here, for the case of an infinite value
with a valid unit and a time zone specified, which would also have
failed with the bug as timestamptz_trunc_internal() is also used by
timestamptz_trunc_zone(), like:
SELECT date_trunc( 'week', timestamp with time zone 'infinity', 'GMT')
AS inf_zone_trunc;
I have added one more test, reversed the order to avoid spurious
failures should one have the idea to do a bisect with a 32b build, and
applied both things.
Thanks for the report!
--
Michael