public inbox for [email protected]  
help / color / mirror / Atom feed
Re: uuidv7 improperly accepts dates before 1970-01-01
5+ messages / 3 participants
[nested] [flat]

* Re: uuidv7 improperly accepts dates before 1970-01-01
@ 2026-04-27 22:51 Christophe Pettus <[email protected]>
  2026-05-28 00:02 ` Re: uuidv7 improperly accepts dates before 1970-01-01 Masahiko Sawada <[email protected]>
  0 siblings, 1 reply; 5+ messages in thread

From: Christophe Pettus @ 2026-04-27 22:51 UTC (permalink / raw)
  To: Andrey Borodin <[email protected]>; +Cc: [email protected]

Hi, Andrey,

Thanks for the response!  I'm moving it to -hackers since it's not really a bug related conversation at this point.  (resending with the right list this time!)

> On Apr 25, 2026, at 05:26, Andrey Borodin <[email protected]> wrote:

> We consulted with RFC authors
> about this feature, and they confirmed that shifting time is compliant with RFC wording.

Time shifting doesn't automatically imply allowing a pre-epoch input time to construct a UUIDv7, though, just that you can construct a UUIDv7 with something other than wall-clock time.

> We wrote the specific test that ensures vast space for shift, but not unlimited.

That's another problem: the API gives the impression of a much larger space than actually exists.

# select uuidv7('100000 years'::interval);   # ~11.2 x total time range in a UUID v7.
               uuidv7                
--------------------------------------
37b45c74-469d-7e1b-9397-1a971a99ab2b
(1 row)

At a minimum, it should reject a shift that creates a time later than a UUID v7 can represent.

> Time shifting would become a footgun if we throw an exception when overflown.

I don't understand why.  If the concern is that someone will pick a value that's close to the maximum, and get a surprising exception when the time overflows that, the right answer is to caution them not to do that rather than permit the wraparound.

And is anyone actually doing this?  Using a very large interval with a large enough number of shards that wraparound is a real possibility?  (In that case, I'd argue they should construct the 48 bit field directly rather than kind of dancing around it by using a time shift.)





^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: uuidv7 improperly accepts dates before 1970-01-01
  2026-04-27 22:51 Re: uuidv7 improperly accepts dates before 1970-01-01 Christophe Pettus <[email protected]>
@ 2026-05-28 00:02 ` Masahiko Sawada <[email protected]>
  2026-05-28 01:00   ` Re: uuidv7 improperly accepts dates before 1970-01-01 Baji Shaik <[email protected]>
  0 siblings, 1 reply; 5+ messages in thread

From: Masahiko Sawada @ 2026-05-28 00:02 UTC (permalink / raw)
  To: Christophe Pettus <[email protected]>; +Cc: Andrey Borodin <[email protected]>; [email protected]

Hi,

On Mon, Apr 27, 2026 at 3:51 PM Christophe Pettus <[email protected]> wrote:
>
> > We wrote the specific test that ensures vast space for shift, but not unlimited.
>
> That's another problem: the API gives the impression of a much larger space than actually exists.
>
> # select uuidv7('100000 years'::interval);   # ~11.2 x total time range in a UUID v7.
>                uuidv7
> --------------------------------------
> 37b45c74-469d-7e1b-9397-1a971a99ab2b
> (1 row)
>

Fair point.

> At a minimum, it should reject a shift that creates a time later than a UUID v7 can represent.

I think that if we add a lower-bound check as the proposed patch does
an upper-bound check should also be added.

>
> > Time shifting would become a footgun if we throw an exception when overflown.
>
> I don't understand why.  If the concern is that someone will pick a value that's close to the maximum, and get a surprising exception when the time overflows that, the right answer is to caution them not to do that rather than permit the wraparound.

I guess that monotonicity could easily be violated depending on how
users shift the wall-clock. Taking Andrey's example, if they use
something like uuidv7('-10 years' * shard_id), the monotonicity would
be broken with just 6 shards.

I guess it would be safer to raise an error in such cases rather than
silently allowing wraparound. Otherwise, users might only realize that
their UUIDv7 values are no longer sortable years down the road, which
would be disastrous. Moreover, raising an error would be consistent
with how PostgreSQL natively handles timestamp + interval overflows.

That said, while I am leaning toward introducing boundary checks, we
should carefully consider this change since it could potentially break
existing applications that rely on the current behavior of
uuidv7(interval).

Regards,

-- 
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com





^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: uuidv7 improperly accepts dates before 1970-01-01
  2026-04-27 22:51 Re: uuidv7 improperly accepts dates before 1970-01-01 Christophe Pettus <[email protected]>
  2026-05-28 00:02 ` Re: uuidv7 improperly accepts dates before 1970-01-01 Masahiko Sawada <[email protected]>
@ 2026-05-28 01:00   ` Baji Shaik <[email protected]>
  2026-06-11 19:20     ` Re: uuidv7 improperly accepts dates before 1970-01-01 Masahiko Sawada <[email protected]>
  0 siblings, 1 reply; 5+ messages in thread

From: Baji Shaik @ 2026-05-28 01:00 UTC (permalink / raw)
  To: Masahiko Sawada <[email protected]>; +Cc: Christophe Pettus <[email protected]>; Andrey Borodin <[email protected]>; [email protected]

Hello Masahiko,

On Wed, May 27, 2026 at 7:02 PM Masahiko Sawada <[email protected]>
wrote:

> I guess it would be safer to raise an error in such cases rather than
> silently allowing wraparound. Otherwise, users might only realize that
> their UUIDv7 values are no longer sortable years down the road, which
> would be disastrous. Moreover, raising an error would be consistent
> with how PostgreSQL natively handles timestamp + interval overflows.
>

+1.  I ran into the same issue while testing, specifically,
uuidv7('infinity'::interval) overflows int64 during the epoch
conversion and produces a UUID with an incorrect timestamp.
There's no valid use case for infinity as a shift offset.

I have a small patch that adds a TIMESTAMP_NOT_FINITE check after
timestamptz_pl_interval(), which catches both infinity and
-infinity.  Happy to extend it to also cover the 48-bit upper/lower
bound checks if we agree on the direction.

Thanks,
Baji Shaik


Attachments:

  [application/octet-stream] 0001-Fix-uuidv7-with-infinite-interval-causing-integer-ov.patch (2.9K, 3-0001-Fix-uuidv7-with-infinite-interval-causing-integer-ov.patch)
  download | inline diff:
From dc28a08b2fe17990051454f4db935a0ef4d023d3 Mon Sep 17 00:00:00 2001
From: Baji Shaik <[email protected]>
Date: Wed, 27 May 2026 19:43:15 -0500
Subject: [PATCH] Fix uuidv7() with infinite interval causing integer overflow

uuidv7('infinity'::interval) and uuidv7('-infinity'::interval) cause
integer overflow when converting the infinite TimestampTz back to a
Unix epoch microsecond value.  timestamptz_pl_interval() returns the
special infinity/negative-infinity TimestampTz values, and the
subsequent addition of the epoch offset overflows int64, producing a
garbage timestamp.

Fix by checking TIMESTAMP_NOT_FINITE(ts) immediately after the
interval arithmetic and rejecting with a clear error.
---
 src/backend/utils/adt/uuid.c       | 7 +++++++
 src/test/regress/expected/uuid.out | 5 +++++
 src/test/regress/sql/uuid.sql      | 4 ++++
 3 files changed, 16 insertions(+)

diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c
index 6ee3752ac78..2bba07bb09c 100644
--- a/src/backend/utils/adt/uuid.c
+++ b/src/backend/utils/adt/uuid.c
@@ -687,6 +687,13 @@ uuidv7_interval(PG_FUNCTION_ARGS)
 												 TimestampTzGetDatum(ts),
 												 IntervalPGetDatum(shift)));
 
+
+	/* Reject infinite timestamps */
+	if (TIMESTAMP_NOT_FINITE(ts))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("timestamp out of range for UUID version 7")));
+
 	/* Convert a TimestampTz value back to an UNIX epoch timestamp */
 	us = ts + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * USECS_PER_SEC;
 
diff --git a/src/test/regress/expected/uuid.out b/src/test/regress/expected/uuid.out
index 9c5dda9e9ab..4388739f462 100644
--- a/src/test/regress/expected/uuid.out
+++ b/src/test/regress/expected/uuid.out
@@ -280,6 +280,11 @@ SELECT uuid_extract_version('11111111-1111-1111-1111-111111111111');  -- null
 ----------------------
                      
 (1 row)
+-- uuidv7(interval) rejects infinite intervals
+SELECT uuidv7('infinity'::interval);
+ERROR:  timestamp out of range for UUID version 7
+SELECT uuidv7('-infinity'::interval);
+ERROR:  timestamp out of range for UUID version 7
 
 SELECT uuid_extract_version(uuidv4());  -- 4
  uuid_extract_version 
diff --git a/src/test/regress/sql/uuid.sql b/src/test/regress/sql/uuid.sql
index 8cc2ad40614..d9f0401880b 100644
--- a/src/test/regress/sql/uuid.sql
+++ b/src/test/regress/sql/uuid.sql
@@ -146,6 +146,10 @@ SELECT y, ts, prev_ts FROM uuidts WHERE ts < prev_ts;
 SELECT uuid_extract_version('11111111-1111-5111-8111-111111111111');  -- 5
 SELECT uuid_extract_version(gen_random_uuid());  -- 4
 SELECT uuid_extract_version('11111111-1111-1111-1111-111111111111');  -- null
+
+-- uuidv7(interval) rejects infinite intervals
+SELECT uuidv7('infinity'::interval);
+SELECT uuidv7('-infinity'::interval);
 SELECT uuid_extract_version(uuidv4());  -- 4
 SELECT uuid_extract_version(uuidv7());  -- 7
 
-- 
2.50.1 (Apple Git-155)



^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: uuidv7 improperly accepts dates before 1970-01-01
  2026-04-27 22:51 Re: uuidv7 improperly accepts dates before 1970-01-01 Christophe Pettus <[email protected]>
  2026-05-28 00:02 ` Re: uuidv7 improperly accepts dates before 1970-01-01 Masahiko Sawada <[email protected]>
  2026-05-28 01:00   ` Re: uuidv7 improperly accepts dates before 1970-01-01 Baji Shaik <[email protected]>
@ 2026-06-11 19:20     ` Masahiko Sawada <[email protected]>
  2026-06-12 22:34       ` Re: uuidv7 improperly accepts dates before 1970-01-01 Baji Shaik <[email protected]>
  0 siblings, 1 reply; 5+ messages in thread

From: Masahiko Sawada @ 2026-06-11 19:20 UTC (permalink / raw)
  To: Baji Shaik <[email protected]>; +Cc: Christophe Pettus <[email protected]>; Andrey Borodin <[email protected]>; [email protected]

On Wed, May 27, 2026 at 6:01 PM Baji Shaik <[email protected]> wrote:
>
> Hello Masahiko,
>
> On Wed, May 27, 2026 at 7:02 PM Masahiko Sawada <[email protected]> wrote:
>>
>> I guess it would be safer to raise an error in such cases rather than
>> silently allowing wraparound. Otherwise, users might only realize that
>> their UUIDv7 values are no longer sortable years down the road, which
>> would be disastrous. Moreover, raising an error would be consistent
>> with how PostgreSQL natively handles timestamp + interval overflows.
>
>
> +1.  I ran into the same issue while testing, specifically,
> uuidv7('infinity'::interval) overflows int64 during the epoch
> conversion and produces a UUID with an incorrect timestamp.
> There's no valid use case for infinity as a shift offset.

Yeah, I think we should reject such cases at least.

> I have a small patch that adds a TIMESTAMP_NOT_FINITE check after
> timestamptz_pl_interval(), which catches both infinity and
> -infinity.  Happy to extend it to also cover the 48-bit upper/lower
> bound checks if we agree on the direction.

I think we should go ahead and add both upper and lower bound checks,
barring objections.

The current behavior silently produces UUIDs with nonsensical
timestamps when the interval causes the result to go before the Unix
epoch or beyond what 48 bits can represent. Regarding backward
compatibility: this change would affect applications using
uuidv7(interval) with values that cause the resulting timestamp to
fall outside the representable range. But I guess that in practice,
any such application is likely already getting incorrect results due
to wraparound, so the risk of breaking working use cases seems very
low.

Regards,

-- 
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com






^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: uuidv7 improperly accepts dates before 1970-01-01
  2026-04-27 22:51 Re: uuidv7 improperly accepts dates before 1970-01-01 Christophe Pettus <[email protected]>
  2026-05-28 00:02 ` Re: uuidv7 improperly accepts dates before 1970-01-01 Masahiko Sawada <[email protected]>
  2026-05-28 01:00   ` Re: uuidv7 improperly accepts dates before 1970-01-01 Baji Shaik <[email protected]>
  2026-06-11 19:20     ` Re: uuidv7 improperly accepts dates before 1970-01-01 Masahiko Sawada <[email protected]>
@ 2026-06-12 22:34       ` Baji Shaik <[email protected]>
  0 siblings, 0 replies; 5+ messages in thread

From: Baji Shaik @ 2026-06-12 22:34 UTC (permalink / raw)
  To: Masahiko Sawada <[email protected]>; +Cc: Christophe Pettus <[email protected]>; Andrey Borodin <[email protected]>; [email protected]

On Thu, Jun 11, 2026 at 2:20 PM Masahiko Sawada <[email protected]>
wrote:

> I think we should go ahead and add both upper and lower bound checks,
> barring objections.
>

Thanks Masahiko. Here's a patch series that adds both boundary
checks along with the infinity check from my earlier patch:

  0001 - Reject timestamps before the Unix epoch (lower bound)
  0002 - Reject infinite intervals
  0003 - Reject timestamps beyond the 48-bit field limit (upper bound)

Christophe's original v1 covered the pre-epoch case; 0001 is
essentially the same fix with slightly different wording. I have
included it here so the series is self-contained and applies
cleanly on HEAD. Happy to drop it in favor of Christophe's
version if you prefer that.

The infinity check (0002) goes before the epoch conversion so
that uuidv7('infinity'::interval) gets a clear "infinite timestamps"
message rather than falling through to the pre-epoch check
with a confusing detail.

All three use ERRCODE_DATETIME_VALUE_OUT_OF_RANGE with errdetail.

Thanks,
Baji Shaik.


Attachments:

  [application/octet-stream] 0001-Fix-uuidv7-with-pre-epoch-interval-silently-producin.patch (3.0K, 3-0001-Fix-uuidv7-with-pre-epoch-interval-silently-producin.patch)
  download | inline diff:
From 22c0770e6e41c7914f3df80baaff846b6c9f589a Mon Sep 17 00:00:00 2001
From: Baji Shaik <[email protected]>
Date: Fri, 12 Jun 2026 17:30:04 -0500
Subject: [PATCH 1/3] Fix uuidv7() with pre-epoch interval silently producing
 misordered UUID

uuidv7(interval) with a negative interval large enough to shift the
timestamp before the Unix epoch (1970-01-01) silently produces a UUID
whose encoded timestamp wraps around via unsigned integer overflow.

Fix by rejecting timestamps before the Unix epoch with a clear error.
This aligns with RFC 9562 Section 6.2.
---
 src/backend/utils/adt/uuid.c       | 11 +++++++++++
 src/test/regress/expected/uuid.out |  4 ++++
 src/test/regress/sql/uuid.sql      |  3 +++
 3 files changed, 18 insertions(+)

diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c
index 6ee3752ac78..c44858b6391 100644
--- a/src/backend/utils/adt/uuid.c
+++ b/src/backend/utils/adt/uuid.c
@@ -690,6 +690,17 @@ uuidv7_interval(PG_FUNCTION_ARGS)
 	/* Convert a TimestampTz value back to an UNIX epoch timestamp */
 	us = ts + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * USECS_PER_SEC;
 
+	/*
+	 * UUID version 7 does not support timestamps before the Unix epoch.
+	 * A negative value here would wrap when cast to uint64, producing a UUID
+	 * with a bogus far-future timestamp that breaks sort ordering.
+	 */
+	if (us < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("timestamp out of range for UUID version 7"),
+				 errdetail("UUID version 7 does not support timestamps before the Unix epoch.")));
+
 	/* Generate an UUIDv7 */
 	uuid = generate_uuidv7(us / US_PER_MS, (us % US_PER_MS) * NS_PER_US + ns % NS_PER_US);
 
diff --git a/src/test/regress/expected/uuid.out b/src/test/regress/expected/uuid.out
index 9c5dda9e9ab..ea3c1adf412 100644
--- a/src/test/regress/expected/uuid.out
+++ b/src/test/regress/expected/uuid.out
@@ -318,6 +318,10 @@ SELECT uuid_extract_timestamp('11111111-1111-1111-1111-111111111111');  -- null
  
 (1 row)
 
+-- uuidv7(interval) rejects timestamps before the Unix epoch
+SELECT uuidv7('-1000 years'::interval);
+ERROR:  timestamp out of range for UUID version 7
+DETAIL:  UUID version 7 does not support timestamps before the Unix epoch.
 -- casts
 SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid::bytea;
                bytea                
diff --git a/src/test/regress/sql/uuid.sql b/src/test/regress/sql/uuid.sql
index 8cc2ad40614..15442f5f9e7 100644
--- a/src/test/regress/sql/uuid.sql
+++ b/src/test/regress/sql/uuid.sql
@@ -155,6 +155,9 @@ SELECT uuid_extract_timestamp('017F22E2-79B0-7CC3-98C4-DC0C0C07398F') = 'Tuesday
 SELECT uuid_extract_timestamp(gen_random_uuid());  -- null
 SELECT uuid_extract_timestamp('11111111-1111-1111-1111-111111111111');  -- null
 
+-- uuidv7(interval) rejects timestamps before the Unix epoch
+SELECT uuidv7('-1000 years'::interval);
+
 -- casts
 SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid::bytea;
 SELECT '\x019a2f859ced7225b99d9c55044a2563'::bytea::uuid;
-- 
2.50.1 (Apple Git-155)



  [application/octet-stream] 0002-Fix-uuidv7-with-infinite-interval-causing-integer-ov.patch (3.2K, 4-0002-Fix-uuidv7-with-infinite-interval-causing-integer-ov.patch)
  download | inline diff:
From fd0656023b900e73475cccad255ffef7d472e00f Mon Sep 17 00:00:00 2001
From: Baji Shaik <[email protected]>
Date: Fri, 12 Jun 2026 17:30:24 -0500
Subject: [PATCH 2/3] Fix uuidv7() with infinite interval causing integer
 overflow

uuidv7('infinity'::interval) overflows int64 during the epoch
conversion and produces a UUID with an incorrect timestamp.

Fix by adding a TIMESTAMP_NOT_FINITE check after
timestamptz_pl_interval(), which catches both infinity and
-infinity before any arithmetic on the timestamp value.
---
 src/backend/utils/adt/uuid.c       | 11 +++++++++++
 src/test/regress/expected/uuid.out |  7 +++++++
 src/test/regress/sql/uuid.sql      |  4 ++++
 3 files changed, 22 insertions(+)

diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c
index c44858b6391..3269bfd922c 100644
--- a/src/backend/utils/adt/uuid.c
+++ b/src/backend/utils/adt/uuid.c
@@ -687,6 +687,17 @@ uuidv7_interval(PG_FUNCTION_ARGS)
 												 TimestampTzGetDatum(ts),
 												 IntervalPGetDatum(shift)));
 
+	/*
+	 * Reject infinite intervals.  timestamptz_pl_interval() can produce an
+	 * infinite timestamp when the input interval is infinite, and converting
+	 * that to a Unix epoch value would overflow.
+	 */
+	if (TIMESTAMP_NOT_FINITE(ts))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("timestamp out of range for UUID version 7"),
+				 errdetail("UUID version 7 does not support infinite timestamps.")));
+
 	/* Convert a TimestampTz value back to an UNIX epoch timestamp */
 	us = ts + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * USECS_PER_SEC;
 
diff --git a/src/test/regress/expected/uuid.out b/src/test/regress/expected/uuid.out
index ea3c1adf412..17693f20639 100644
--- a/src/test/regress/expected/uuid.out
+++ b/src/test/regress/expected/uuid.out
@@ -322,6 +322,13 @@ SELECT uuid_extract_timestamp('11111111-1111-1111-1111-111111111111');  -- null
 SELECT uuidv7('-1000 years'::interval);
 ERROR:  timestamp out of range for UUID version 7
 DETAIL:  UUID version 7 does not support timestamps before the Unix epoch.
+-- uuidv7(interval) rejects infinite intervals
+SELECT uuidv7('infinity'::interval);
+ERROR:  timestamp out of range for UUID version 7
+DETAIL:  UUID version 7 does not support infinite timestamps.
+SELECT uuidv7('-infinity'::interval);
+ERROR:  timestamp out of range for UUID version 7
+DETAIL:  UUID version 7 does not support infinite timestamps.
 -- casts
 SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid::bytea;
                bytea                
diff --git a/src/test/regress/sql/uuid.sql b/src/test/regress/sql/uuid.sql
index 15442f5f9e7..e88696ee793 100644
--- a/src/test/regress/sql/uuid.sql
+++ b/src/test/regress/sql/uuid.sql
@@ -158,6 +158,10 @@ SELECT uuid_extract_timestamp('11111111-1111-1111-1111-111111111111');  -- null
 -- uuidv7(interval) rejects timestamps before the Unix epoch
 SELECT uuidv7('-1000 years'::interval);
 
+-- uuidv7(interval) rejects infinite intervals
+SELECT uuidv7('infinity'::interval);
+SELECT uuidv7('-infinity'::interval);
+
 -- casts
 SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid::bytea;
 SELECT '\x019a2f859ced7225b99d9c55044a2563'::bytea::uuid;
-- 
2.50.1 (Apple Git-155)



  [application/octet-stream] 0003-Fix-uuidv7-with-far-future-interval-silently-overflo.patch (3.1K, 5-0003-Fix-uuidv7-with-far-future-interval-silently-overflo.patch)
  download | inline diff:
From 4cf381e8ad3604c00c2e506bc89d2fa46625df63 Mon Sep 17 00:00:00 2001
From: Baji Shaik <[email protected]>
Date: Fri, 12 Jun 2026 17:30:47 -0500
Subject: [PATCH 3/3] Fix uuidv7() with far-future interval silently
 overflowing 48-bit timestamp

uuidv7(interval) with a large positive interval that pushes the
timestamp beyond approximately year 10889 silently produces a UUID
with a truncated timestamp.  The UUID version 7 timestamp field is
48 bits wide; when the millisecond value exceeds 2^48, the upper
bits are silently discarded, and the resulting UUID breaks sort ordering.

Fix by rejecting timestamps that would overflow the 48-bit field.
---
 src/backend/utils/adt/uuid.c       | 11 +++++++++++
 src/test/regress/expected/uuid.out |  4 ++++
 src/test/regress/sql/uuid.sql      |  3 +++
 3 files changed, 18 insertions(+)

diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c
index 3269bfd922c..4bec3de1ad2 100644
--- a/src/backend/utils/adt/uuid.c
+++ b/src/backend/utils/adt/uuid.c
@@ -712,6 +712,17 @@ uuidv7_interval(PG_FUNCTION_ARGS)
 				 errmsg("timestamp out of range for UUID version 7"),
 				 errdetail("UUID version 7 does not support timestamps before the Unix epoch.")));
 
+	/*
+	 * The UUID version 7 timestamp field is 48 bits wide, storing
+	 * milliseconds since the Unix epoch.  Reject timestamps that would
+	 * overflow this field (dates beyond approximately year 10889).
+	 */
+	if (us / US_PER_MS > (int64) 0xFFFFFFFFFFFF)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+				 errmsg("timestamp out of range for UUID version 7"),
+				 errdetail("UUID version 7 does not support timestamps beyond approximately year 10889.")));
+
 	/* Generate an UUIDv7 */
 	uuid = generate_uuidv7(us / US_PER_MS, (us % US_PER_MS) * NS_PER_US + ns % NS_PER_US);
 
diff --git a/src/test/regress/expected/uuid.out b/src/test/regress/expected/uuid.out
index 17693f20639..589f0b6ea42 100644
--- a/src/test/regress/expected/uuid.out
+++ b/src/test/regress/expected/uuid.out
@@ -329,6 +329,10 @@ DETAIL:  UUID version 7 does not support infinite timestamps.
 SELECT uuidv7('-infinity'::interval);
 ERROR:  timestamp out of range for UUID version 7
 DETAIL:  UUID version 7 does not support infinite timestamps.
+-- uuidv7(interval) rejects timestamps that overflow the 48-bit field
+SELECT uuidv7('8920 years'::interval);
+ERROR:  timestamp out of range for UUID version 7
+DETAIL:  UUID version 7 does not support timestamps beyond approximately year 10889.
 -- casts
 SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid::bytea;
                bytea                
diff --git a/src/test/regress/sql/uuid.sql b/src/test/regress/sql/uuid.sql
index e88696ee793..3fcc68110bf 100644
--- a/src/test/regress/sql/uuid.sql
+++ b/src/test/regress/sql/uuid.sql
@@ -162,6 +162,9 @@ SELECT uuidv7('-1000 years'::interval);
 SELECT uuidv7('infinity'::interval);
 SELECT uuidv7('-infinity'::interval);
 
+-- uuidv7(interval) rejects timestamps that overflow the 48-bit field
+SELECT uuidv7('8920 years'::interval);
+
 -- casts
 SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid::bytea;
 SELECT '\x019a2f859ced7225b99d9c55044a2563'::bytea::uuid;
-- 
2.50.1 (Apple Git-155)



^ permalink  raw  reply  [nested|flat] 5+ messages in thread


end of thread, other threads:[~2026-06-12 22:34 UTC | newest]

Thread overview: 5+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-04-27 22:51 Re: uuidv7 improperly accepts dates before 1970-01-01 Christophe Pettus <[email protected]>
2026-05-28 00:02 ` Masahiko Sawada <[email protected]>
2026-05-28 01:00   ` Baji Shaik <[email protected]>
2026-06-11 19:20     ` Masahiko Sawada <[email protected]>
2026-06-12 22:34       ` Baji Shaik <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox