From 8b52d0942f657c35e238bd95bd2d95aa5c4a5b2e Mon Sep 17 00:00:00 2001 From: Andrey Borodin Date: Fri, 31 Jan 2025 12:03:16 +0500 Subject: [PATCH] UUDv7: fix offset computations in dates after 2262 We used nanosecond representation of offsetted time values which cannot be stored in 64-bit integer for dates significantly after beginning of UNIX epoch. To prevent overflow we separate millisecond part from nanoseconds, thus allowing us to store both parts in 64-bit integers. --- src/backend/utils/adt/uuid.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c index 4f8402ef925..3349c2674c8 100644 --- a/src/backend/utils/adt/uuid.c +++ b/src/backend/utils/adt/uuid.c @@ -68,7 +68,7 @@ static int uuid_fast_cmp(Datum x, Datum y, SortSupport ssup); static bool uuid_abbrev_abort(int memtupcount, SortSupport ssup); static Datum uuid_abbrev_convert(Datum original, SortSupport ssup); static inline void uuid_set_version(pg_uuid_t *uuid, unsigned char version); -static inline int64 get_real_time_ns_ascending(); +static inline uint64 get_real_time_ns_ascending(); Datum uuid_in(PG_FUNCTION_ARGS) @@ -476,11 +476,11 @@ gen_random_uuid(PG_FUNCTION_ARGS) * The returned timestamp is ensured to be at least SUBMS_MINIMAL_STEP greater * than the previous returned timestamp (on this backend). */ -static inline int64 +static inline uint64 get_real_time_ns_ascending() { - static int64 previous_ns = 0; - int64 ns; + static uint64 previous_ns = 0; + uint64 ns; /* Get the current real timestamp */ @@ -527,13 +527,13 @@ get_real_time_ns_ascending() * used for time-dependent bits of UUID. */ static pg_uuid_t * -generate_uuidv7(int64 ns) +generate_uuidv7(uint64 ms, uint64 ns_in_ms) { pg_uuid_t *uuid = palloc(UUID_LEN); int64 unix_ts_ms; int32 increased_clock_precision; - unix_ts_ms = ns / NS_PER_MS; + unix_ts_ms = ms; /* Fill in time part */ uuid->data[0] = (unsigned char) (unix_ts_ms >> 40); @@ -547,7 +547,7 @@ generate_uuidv7(int64 ns) * sub-millisecond timestamp fraction (SUBMS_BITS bits, not * SUBMS_MINIMAL_STEP_BITS) */ - increased_clock_precision = ((ns % NS_PER_MS) * (1 << SUBMS_BITS)) / NS_PER_MS; + increased_clock_precision = ((ns_in_ms) * (1 << SUBMS_BITS)) / NS_PER_MS; /* Fill the increased clock precision to "rand_a" bits */ uuid->data[6] = (unsigned char) (increased_clock_precision >> 8); @@ -586,7 +586,8 @@ generate_uuidv7(int64 ns) Datum uuidv7(PG_FUNCTION_ARGS) { - pg_uuid_t *uuid = generate_uuidv7(get_real_time_ns_ascending()); + uint64 ns = get_real_time_ns_ascending(); + pg_uuid_t *uuid = generate_uuidv7(ns / NS_PER_MS, ns % NS_PER_MS); PG_RETURN_UUID_P(uuid); } @@ -600,7 +601,9 @@ uuidv7_interval(PG_FUNCTION_ARGS) Interval *shift = PG_GETARG_INTERVAL_P(0); TimestampTz ts; pg_uuid_t *uuid; - int64 ns = get_real_time_ns_ascending(); + /* 64 bits is enough for real time, but not for a time range of UUID */ + uint64 ns = get_real_time_ns_ascending(); + uint64 us; /* * Shift the current timestamp by the given interval. To calculate time @@ -621,11 +624,10 @@ uuidv7_interval(PG_FUNCTION_ARGS) /* * Convert a TimestampTz value back to an UNIX epoch and back nanoseconds. */ - ns = (ts + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * USECS_PER_SEC) - * NS_PER_US + ns % NS_PER_US; + us = (ts + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * USECS_PER_SEC); /* Generate an UUIDv7 */ - uuid = generate_uuidv7(ns); + uuid = generate_uuidv7(us / 1000, (us % 1000) * 1000 + ns % NS_PER_US); PG_RETURN_UUID_P(uuid); } -- 2.42.0