/src/postgres/src/backend/utils/adt/uuid.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * uuid.c |
4 | | * Functions for the built-in type "uuid". |
5 | | * |
6 | | * Copyright (c) 2007-2025, PostgreSQL Global Development Group |
7 | | * |
8 | | * IDENTIFICATION |
9 | | * src/backend/utils/adt/uuid.c |
10 | | * |
11 | | *------------------------------------------------------------------------- |
12 | | */ |
13 | | |
14 | | #include "postgres.h" |
15 | | |
16 | | #include <limits.h> |
17 | | #include <time.h> /* for clock_gettime() */ |
18 | | |
19 | | #include "common/hashfn.h" |
20 | | #include "lib/hyperloglog.h" |
21 | | #include "libpq/pqformat.h" |
22 | | #include "port/pg_bswap.h" |
23 | | #include "utils/fmgrprotos.h" |
24 | | #include "utils/guc.h" |
25 | | #include "utils/skipsupport.h" |
26 | | #include "utils/sortsupport.h" |
27 | | #include "utils/timestamp.h" |
28 | | #include "utils/uuid.h" |
29 | | |
30 | | /* helper macros */ |
31 | 0 | #define NS_PER_S INT64CONST(1000000000) |
32 | 0 | #define NS_PER_MS INT64CONST(1000000) |
33 | 0 | #define NS_PER_US INT64CONST(1000) |
34 | 0 | #define US_PER_MS INT64CONST(1000) |
35 | | |
36 | | /* |
37 | | * UUID version 7 uses 12 bits in "rand_a" to store 1/4096 (or 2^12) fractions of |
38 | | * sub-millisecond. While most Unix-like platforms provide nanosecond-precision |
39 | | * timestamps, some systems only offer microsecond precision, limiting us to 10 |
40 | | * bits of sub-millisecond information. For example, on macOS, real time is |
41 | | * truncated to microseconds. Additionally, MSVC uses the ported version of |
42 | | * gettimeofday() that returns microsecond precision. |
43 | | * |
44 | | * On systems with only 10 bits of sub-millisecond precision, we still use |
45 | | * 1/4096 parts of a millisecond, but fill lower 2 bits with random numbers |
46 | | * (see generate_uuidv7() for details). |
47 | | * |
48 | | * SUBMS_MINIMAL_STEP_NS defines the minimum number of nanoseconds that guarantees |
49 | | * an increase in the UUID's clock precision. |
50 | | */ |
51 | | #if defined(__darwin__) || defined(_MSC_VER) |
52 | | #define SUBMS_MINIMAL_STEP_BITS 10 |
53 | | #else |
54 | 0 | #define SUBMS_MINIMAL_STEP_BITS 12 |
55 | | #endif |
56 | 0 | #define SUBMS_BITS 12 |
57 | 0 | #define SUBMS_MINIMAL_STEP_NS ((NS_PER_MS / (1 << SUBMS_MINIMAL_STEP_BITS)) + 1) |
58 | | |
59 | | /* sortsupport for uuid */ |
60 | | typedef struct |
61 | | { |
62 | | int64 input_count; /* number of non-null values seen */ |
63 | | bool estimating; /* true if estimating cardinality */ |
64 | | |
65 | | hyperLogLogState abbr_card; /* cardinality estimator */ |
66 | | } uuid_sortsupport_state; |
67 | | |
68 | | static void string_to_uuid(const char *source, pg_uuid_t *uuid, Node *escontext); |
69 | | static int uuid_internal_cmp(const pg_uuid_t *arg1, const pg_uuid_t *arg2); |
70 | | static int uuid_fast_cmp(Datum x, Datum y, SortSupport ssup); |
71 | | static bool uuid_abbrev_abort(int memtupcount, SortSupport ssup); |
72 | | static Datum uuid_abbrev_convert(Datum original, SortSupport ssup); |
73 | | static inline void uuid_set_version(pg_uuid_t *uuid, unsigned char version); |
74 | | static inline int64 get_real_time_ns_ascending(); |
75 | | static pg_uuid_t *generate_uuidv7(uint64 unix_ts_ms, uint32 sub_ms); |
76 | | |
77 | | Datum |
78 | | uuid_in(PG_FUNCTION_ARGS) |
79 | 0 | { |
80 | 0 | char *uuid_str = PG_GETARG_CSTRING(0); |
81 | 0 | pg_uuid_t *uuid; |
82 | |
|
83 | 0 | uuid = (pg_uuid_t *) palloc(sizeof(*uuid)); |
84 | 0 | string_to_uuid(uuid_str, uuid, fcinfo->context); |
85 | 0 | PG_RETURN_UUID_P(uuid); |
86 | 0 | } |
87 | | |
88 | | Datum |
89 | | uuid_out(PG_FUNCTION_ARGS) |
90 | 0 | { |
91 | 0 | pg_uuid_t *uuid = PG_GETARG_UUID_P(0); |
92 | 0 | static const char hex_chars[] = "0123456789abcdef"; |
93 | 0 | char *buf, |
94 | 0 | *p; |
95 | 0 | int i; |
96 | | |
97 | | /* counts for the four hyphens and the zero-terminator */ |
98 | 0 | buf = palloc(2 * UUID_LEN + 5); |
99 | 0 | p = buf; |
100 | 0 | for (i = 0; i < UUID_LEN; i++) |
101 | 0 | { |
102 | 0 | int hi; |
103 | 0 | int lo; |
104 | | |
105 | | /* |
106 | | * We print uuid values as a string of 8, 4, 4, 4, and then 12 |
107 | | * hexadecimal characters, with each group is separated by a hyphen |
108 | | * ("-"). Therefore, add the hyphens at the appropriate places here. |
109 | | */ |
110 | 0 | if (i == 4 || i == 6 || i == 8 || i == 10) |
111 | 0 | *p++ = '-'; |
112 | |
|
113 | 0 | hi = uuid->data[i] >> 4; |
114 | 0 | lo = uuid->data[i] & 0x0F; |
115 | |
|
116 | 0 | *p++ = hex_chars[hi]; |
117 | 0 | *p++ = hex_chars[lo]; |
118 | 0 | } |
119 | 0 | *p = '\0'; |
120 | |
|
121 | 0 | PG_RETURN_CSTRING(buf); |
122 | 0 | } |
123 | | |
124 | | /* |
125 | | * We allow UUIDs as a series of 32 hexadecimal digits with an optional dash |
126 | | * after each group of 4 hexadecimal digits, and optionally surrounded by {}. |
127 | | * (The canonical format 8x-4x-4x-4x-12x, where "nx" means n hexadecimal |
128 | | * digits, is the only one used for output.) |
129 | | */ |
130 | | static void |
131 | | string_to_uuid(const char *source, pg_uuid_t *uuid, Node *escontext) |
132 | 0 | { |
133 | 0 | const char *src = source; |
134 | 0 | bool braces = false; |
135 | 0 | int i; |
136 | |
|
137 | 0 | if (src[0] == '{') |
138 | 0 | { |
139 | 0 | src++; |
140 | 0 | braces = true; |
141 | 0 | } |
142 | |
|
143 | 0 | for (i = 0; i < UUID_LEN; i++) |
144 | 0 | { |
145 | 0 | char str_buf[3]; |
146 | |
|
147 | 0 | if (src[0] == '\0' || src[1] == '\0') |
148 | 0 | goto syntax_error; |
149 | 0 | memcpy(str_buf, src, 2); |
150 | 0 | if (!isxdigit((unsigned char) str_buf[0]) || |
151 | 0 | !isxdigit((unsigned char) str_buf[1])) |
152 | 0 | goto syntax_error; |
153 | | |
154 | 0 | str_buf[2] = '\0'; |
155 | 0 | uuid->data[i] = (unsigned char) strtoul(str_buf, NULL, 16); |
156 | 0 | src += 2; |
157 | 0 | if (src[0] == '-' && (i % 2) == 1 && i < UUID_LEN - 1) |
158 | 0 | src++; |
159 | 0 | } |
160 | | |
161 | 0 | if (braces) |
162 | 0 | { |
163 | 0 | if (*src != '}') |
164 | 0 | goto syntax_error; |
165 | 0 | src++; |
166 | 0 | } |
167 | | |
168 | 0 | if (*src != '\0') |
169 | 0 | goto syntax_error; |
170 | | |
171 | 0 | return; |
172 | | |
173 | 0 | syntax_error: |
174 | 0 | ereturn(escontext,, |
175 | 0 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), |
176 | 0 | errmsg("invalid input syntax for type %s: \"%s\"", |
177 | 0 | "uuid", source))); |
178 | 0 | } |
179 | | |
180 | | Datum |
181 | | uuid_recv(PG_FUNCTION_ARGS) |
182 | 0 | { |
183 | 0 | StringInfo buffer = (StringInfo) PG_GETARG_POINTER(0); |
184 | 0 | pg_uuid_t *uuid; |
185 | |
|
186 | 0 | uuid = (pg_uuid_t *) palloc(UUID_LEN); |
187 | 0 | memcpy(uuid->data, pq_getmsgbytes(buffer, UUID_LEN), UUID_LEN); |
188 | 0 | PG_RETURN_POINTER(uuid); |
189 | 0 | } |
190 | | |
191 | | Datum |
192 | | uuid_send(PG_FUNCTION_ARGS) |
193 | 0 | { |
194 | 0 | pg_uuid_t *uuid = PG_GETARG_UUID_P(0); |
195 | 0 | StringInfoData buffer; |
196 | |
|
197 | 0 | pq_begintypsend(&buffer); |
198 | 0 | pq_sendbytes(&buffer, uuid->data, UUID_LEN); |
199 | 0 | PG_RETURN_BYTEA_P(pq_endtypsend(&buffer)); |
200 | 0 | } |
201 | | |
202 | | /* internal uuid compare function */ |
203 | | static int |
204 | | uuid_internal_cmp(const pg_uuid_t *arg1, const pg_uuid_t *arg2) |
205 | 0 | { |
206 | 0 | return memcmp(arg1->data, arg2->data, UUID_LEN); |
207 | 0 | } |
208 | | |
209 | | Datum |
210 | | uuid_lt(PG_FUNCTION_ARGS) |
211 | 0 | { |
212 | 0 | pg_uuid_t *arg1 = PG_GETARG_UUID_P(0); |
213 | 0 | pg_uuid_t *arg2 = PG_GETARG_UUID_P(1); |
214 | |
|
215 | 0 | PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) < 0); |
216 | 0 | } |
217 | | |
218 | | Datum |
219 | | uuid_le(PG_FUNCTION_ARGS) |
220 | 0 | { |
221 | 0 | pg_uuid_t *arg1 = PG_GETARG_UUID_P(0); |
222 | 0 | pg_uuid_t *arg2 = PG_GETARG_UUID_P(1); |
223 | |
|
224 | 0 | PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) <= 0); |
225 | 0 | } |
226 | | |
227 | | Datum |
228 | | uuid_eq(PG_FUNCTION_ARGS) |
229 | 0 | { |
230 | 0 | pg_uuid_t *arg1 = PG_GETARG_UUID_P(0); |
231 | 0 | pg_uuid_t *arg2 = PG_GETARG_UUID_P(1); |
232 | |
|
233 | 0 | PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) == 0); |
234 | 0 | } |
235 | | |
236 | | Datum |
237 | | uuid_ge(PG_FUNCTION_ARGS) |
238 | 0 | { |
239 | 0 | pg_uuid_t *arg1 = PG_GETARG_UUID_P(0); |
240 | 0 | pg_uuid_t *arg2 = PG_GETARG_UUID_P(1); |
241 | |
|
242 | 0 | PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) >= 0); |
243 | 0 | } |
244 | | |
245 | | Datum |
246 | | uuid_gt(PG_FUNCTION_ARGS) |
247 | 0 | { |
248 | 0 | pg_uuid_t *arg1 = PG_GETARG_UUID_P(0); |
249 | 0 | pg_uuid_t *arg2 = PG_GETARG_UUID_P(1); |
250 | |
|
251 | 0 | PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) > 0); |
252 | 0 | } |
253 | | |
254 | | Datum |
255 | | uuid_ne(PG_FUNCTION_ARGS) |
256 | 0 | { |
257 | 0 | pg_uuid_t *arg1 = PG_GETARG_UUID_P(0); |
258 | 0 | pg_uuid_t *arg2 = PG_GETARG_UUID_P(1); |
259 | |
|
260 | 0 | PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) != 0); |
261 | 0 | } |
262 | | |
263 | | /* handler for btree index operator */ |
264 | | Datum |
265 | | uuid_cmp(PG_FUNCTION_ARGS) |
266 | 0 | { |
267 | 0 | pg_uuid_t *arg1 = PG_GETARG_UUID_P(0); |
268 | 0 | pg_uuid_t *arg2 = PG_GETARG_UUID_P(1); |
269 | |
|
270 | 0 | PG_RETURN_INT32(uuid_internal_cmp(arg1, arg2)); |
271 | 0 | } |
272 | | |
273 | | /* |
274 | | * Sort support strategy routine |
275 | | */ |
276 | | Datum |
277 | | uuid_sortsupport(PG_FUNCTION_ARGS) |
278 | 0 | { |
279 | 0 | SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); |
280 | |
|
281 | 0 | ssup->comparator = uuid_fast_cmp; |
282 | 0 | ssup->ssup_extra = NULL; |
283 | |
|
284 | 0 | if (ssup->abbreviate) |
285 | 0 | { |
286 | 0 | uuid_sortsupport_state *uss; |
287 | 0 | MemoryContext oldcontext; |
288 | |
|
289 | 0 | oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt); |
290 | |
|
291 | 0 | uss = palloc(sizeof(uuid_sortsupport_state)); |
292 | 0 | uss->input_count = 0; |
293 | 0 | uss->estimating = true; |
294 | 0 | initHyperLogLog(&uss->abbr_card, 10); |
295 | |
|
296 | 0 | ssup->ssup_extra = uss; |
297 | |
|
298 | 0 | ssup->comparator = ssup_datum_unsigned_cmp; |
299 | 0 | ssup->abbrev_converter = uuid_abbrev_convert; |
300 | 0 | ssup->abbrev_abort = uuid_abbrev_abort; |
301 | 0 | ssup->abbrev_full_comparator = uuid_fast_cmp; |
302 | |
|
303 | 0 | MemoryContextSwitchTo(oldcontext); |
304 | 0 | } |
305 | |
|
306 | 0 | PG_RETURN_VOID(); |
307 | 0 | } |
308 | | |
309 | | /* |
310 | | * SortSupport comparison func |
311 | | */ |
312 | | static int |
313 | | uuid_fast_cmp(Datum x, Datum y, SortSupport ssup) |
314 | 0 | { |
315 | 0 | pg_uuid_t *arg1 = DatumGetUUIDP(x); |
316 | 0 | pg_uuid_t *arg2 = DatumGetUUIDP(y); |
317 | |
|
318 | 0 | return uuid_internal_cmp(arg1, arg2); |
319 | 0 | } |
320 | | |
321 | | /* |
322 | | * Callback for estimating effectiveness of abbreviated key optimization. |
323 | | * |
324 | | * We pay no attention to the cardinality of the non-abbreviated data, because |
325 | | * there is no equality fast-path within authoritative uuid comparator. |
326 | | */ |
327 | | static bool |
328 | | uuid_abbrev_abort(int memtupcount, SortSupport ssup) |
329 | | { |
330 | | uuid_sortsupport_state *uss = ssup->ssup_extra; |
331 | | double abbr_card; |
332 | | |
333 | | if (memtupcount < 10000 || uss->input_count < 10000 || !uss->estimating) |
334 | | return false; |
335 | | |
336 | | abbr_card = estimateHyperLogLog(&uss->abbr_card); |
337 | | |
338 | | /* |
339 | | * If we have >100k distinct values, then even if we were sorting many |
340 | | * billion rows we'd likely still break even, and the penalty of undoing |
341 | | * that many rows of abbrevs would probably not be worth it. Stop even |
342 | | * counting at that point. |
343 | | */ |
344 | | if (abbr_card > 100000.0) |
345 | | { |
346 | | if (trace_sort) |
347 | | elog(LOG, |
348 | | "uuid_abbrev: estimation ends at cardinality %f" |
349 | | " after " INT64_FORMAT " values (%d rows)", |
350 | | abbr_card, uss->input_count, memtupcount); |
351 | | uss->estimating = false; |
352 | | return false; |
353 | | } |
354 | | |
355 | | /* |
356 | | * Target minimum cardinality is 1 per ~2k of non-null inputs. 0.5 row |
357 | | * fudge factor allows us to abort earlier on genuinely pathological data |
358 | | * where we've had exactly one abbreviated value in the first 2k |
359 | | * (non-null) rows. |
360 | | */ |
361 | | if (abbr_card < uss->input_count / 2000.0 + 0.5) |
362 | | { |
363 | | if (trace_sort) |
364 | | elog(LOG, |
365 | | "uuid_abbrev: aborting abbreviation at cardinality %f" |
366 | | " below threshold %f after " INT64_FORMAT " values (%d rows)", |
367 | | abbr_card, uss->input_count / 2000.0 + 0.5, uss->input_count, |
368 | | memtupcount); |
369 | | return true; |
370 | | } |
371 | | |
372 | | if (trace_sort) |
373 | | elog(LOG, |
374 | | "uuid_abbrev: cardinality %f after " INT64_FORMAT |
375 | | " values (%d rows)", abbr_card, uss->input_count, memtupcount); |
376 | | |
377 | | return false; |
378 | | } |
379 | | |
380 | | /* |
381 | | * Conversion routine for sortsupport. Converts original uuid representation |
382 | | * to abbreviated key representation. Our encoding strategy is simple -- pack |
383 | | * the first `sizeof(Datum)` bytes of uuid data into a Datum (on little-endian |
384 | | * machines, the bytes are stored in reverse order), and treat it as an |
385 | | * unsigned integer. |
386 | | */ |
387 | | static Datum |
388 | | uuid_abbrev_convert(Datum original, SortSupport ssup) |
389 | 0 | { |
390 | 0 | uuid_sortsupport_state *uss = ssup->ssup_extra; |
391 | 0 | pg_uuid_t *authoritative = DatumGetUUIDP(original); |
392 | 0 | Datum res; |
393 | |
|
394 | 0 | memcpy(&res, authoritative->data, sizeof(Datum)); |
395 | 0 | uss->input_count += 1; |
396 | |
|
397 | 0 | if (uss->estimating) |
398 | 0 | { |
399 | 0 | uint32 tmp; |
400 | |
|
401 | 0 | tmp = DatumGetUInt32(res) ^ (uint32) (DatumGetUInt64(res) >> 32); |
402 | |
|
403 | 0 | addHyperLogLog(&uss->abbr_card, DatumGetUInt32(hash_uint32(tmp))); |
404 | 0 | } |
405 | | |
406 | | /* |
407 | | * Byteswap on little-endian machines. |
408 | | * |
409 | | * This is needed so that ssup_datum_unsigned_cmp() (an unsigned integer |
410 | | * 3-way comparator) works correctly on all platforms. If we didn't do |
411 | | * this, the comparator would have to call memcmp() with a pair of |
412 | | * pointers to the first byte of each abbreviated key, which is slower. |
413 | | */ |
414 | 0 | res = DatumBigEndianToNative(res); |
415 | |
|
416 | 0 | return res; |
417 | 0 | } |
418 | | |
419 | | static Datum |
420 | | uuid_decrement(Relation rel, Datum existing, bool *underflow) |
421 | 0 | { |
422 | 0 | pg_uuid_t *uuid; |
423 | |
|
424 | 0 | uuid = (pg_uuid_t *) palloc(UUID_LEN); |
425 | 0 | memcpy(uuid, DatumGetUUIDP(existing), UUID_LEN); |
426 | 0 | for (int i = UUID_LEN - 1; i >= 0; i--) |
427 | 0 | { |
428 | 0 | if (uuid->data[i] > 0) |
429 | 0 | { |
430 | 0 | uuid->data[i]--; |
431 | 0 | *underflow = false; |
432 | 0 | return UUIDPGetDatum(uuid); |
433 | 0 | } |
434 | 0 | uuid->data[i] = UCHAR_MAX; |
435 | 0 | } |
436 | | |
437 | 0 | pfree(uuid); /* cannot leak memory */ |
438 | | |
439 | | /* return value is undefined */ |
440 | 0 | *underflow = true; |
441 | 0 | return (Datum) 0; |
442 | 0 | } |
443 | | |
444 | | static Datum |
445 | | uuid_increment(Relation rel, Datum existing, bool *overflow) |
446 | 0 | { |
447 | 0 | pg_uuid_t *uuid; |
448 | |
|
449 | 0 | uuid = (pg_uuid_t *) palloc(UUID_LEN); |
450 | 0 | memcpy(uuid, DatumGetUUIDP(existing), UUID_LEN); |
451 | 0 | for (int i = UUID_LEN - 1; i >= 0; i--) |
452 | 0 | { |
453 | 0 | if (uuid->data[i] < UCHAR_MAX) |
454 | 0 | { |
455 | 0 | uuid->data[i]++; |
456 | 0 | *overflow = false; |
457 | 0 | return UUIDPGetDatum(uuid); |
458 | 0 | } |
459 | 0 | uuid->data[i] = 0; |
460 | 0 | } |
461 | | |
462 | 0 | pfree(uuid); /* cannot leak memory */ |
463 | | |
464 | | /* return value is undefined */ |
465 | 0 | *overflow = true; |
466 | 0 | return (Datum) 0; |
467 | 0 | } |
468 | | |
469 | | Datum |
470 | | uuid_skipsupport(PG_FUNCTION_ARGS) |
471 | 0 | { |
472 | 0 | SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0); |
473 | 0 | pg_uuid_t *uuid_min = palloc(UUID_LEN); |
474 | 0 | pg_uuid_t *uuid_max = palloc(UUID_LEN); |
475 | |
|
476 | 0 | memset(uuid_min->data, 0x00, UUID_LEN); |
477 | 0 | memset(uuid_max->data, 0xFF, UUID_LEN); |
478 | |
|
479 | 0 | sksup->decrement = uuid_decrement; |
480 | 0 | sksup->increment = uuid_increment; |
481 | 0 | sksup->low_elem = UUIDPGetDatum(uuid_min); |
482 | 0 | sksup->high_elem = UUIDPGetDatum(uuid_max); |
483 | |
|
484 | 0 | PG_RETURN_VOID(); |
485 | 0 | } |
486 | | |
487 | | /* hash index support */ |
488 | | Datum |
489 | | uuid_hash(PG_FUNCTION_ARGS) |
490 | 0 | { |
491 | 0 | pg_uuid_t *key = PG_GETARG_UUID_P(0); |
492 | |
|
493 | 0 | return hash_any(key->data, UUID_LEN); |
494 | 0 | } |
495 | | |
496 | | Datum |
497 | | uuid_hash_extended(PG_FUNCTION_ARGS) |
498 | 0 | { |
499 | 0 | pg_uuid_t *key = PG_GETARG_UUID_P(0); |
500 | |
|
501 | 0 | return hash_any_extended(key->data, UUID_LEN, PG_GETARG_INT64(1)); |
502 | 0 | } |
503 | | |
504 | | /* |
505 | | * Set the given UUID version and the variant bits |
506 | | */ |
507 | | static inline void |
508 | | uuid_set_version(pg_uuid_t *uuid, unsigned char version) |
509 | 0 | { |
510 | | /* set version field, top four bits */ |
511 | 0 | uuid->data[6] = (uuid->data[6] & 0x0f) | (version << 4); |
512 | | |
513 | | /* set variant field, top two bits are 1, 0 */ |
514 | 0 | uuid->data[8] = (uuid->data[8] & 0x3f) | 0x80; |
515 | 0 | } |
516 | | |
517 | | /* |
518 | | * Generate UUID version 4. |
519 | | * |
520 | | * All UUID bytes are filled with strong random numbers except version and |
521 | | * variant bits. |
522 | | */ |
523 | | Datum |
524 | | gen_random_uuid(PG_FUNCTION_ARGS) |
525 | 0 | { |
526 | 0 | pg_uuid_t *uuid = palloc(UUID_LEN); |
527 | |
|
528 | 0 | if (!pg_strong_random(uuid, UUID_LEN)) |
529 | 0 | ereport(ERROR, |
530 | 0 | (errcode(ERRCODE_INTERNAL_ERROR), |
531 | 0 | errmsg("could not generate random values"))); |
532 | | |
533 | | /* |
534 | | * Set magic numbers for a "version 4" (pseudorandom) UUID and variant, |
535 | | * see https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-4 |
536 | | */ |
537 | 0 | uuid_set_version(uuid, 4); |
538 | |
|
539 | 0 | PG_RETURN_UUID_P(uuid); |
540 | 0 | } |
541 | | |
542 | | /* |
543 | | * Get the current timestamp with nanosecond precision for UUID generation. |
544 | | * The returned timestamp is ensured to be at least SUBMS_MINIMAL_STEP greater |
545 | | * than the previous returned timestamp (on this backend). |
546 | | */ |
547 | | static inline int64 |
548 | | get_real_time_ns_ascending() |
549 | 0 | { |
550 | 0 | static int64 previous_ns = 0; |
551 | 0 | int64 ns; |
552 | | |
553 | | /* Get the current real timestamp */ |
554 | |
|
555 | | #ifdef _MSC_VER |
556 | | struct timeval tmp; |
557 | | |
558 | | gettimeofday(&tmp, NULL); |
559 | | ns = tmp.tv_sec * NS_PER_S + tmp.tv_usec * NS_PER_US; |
560 | | #else |
561 | 0 | struct timespec tmp; |
562 | | |
563 | | /* |
564 | | * We don't use gettimeofday(), instead use clock_gettime() with |
565 | | * CLOCK_REALTIME where available in order to get a high-precision |
566 | | * (nanoseconds) real timestamp. |
567 | | * |
568 | | * Note while a timestamp returned by clock_gettime() with CLOCK_REALTIME |
569 | | * is nanosecond-precision on most Unix-like platforms, on some platforms |
570 | | * such as macOS it's restricted to microsecond-precision. |
571 | | */ |
572 | 0 | clock_gettime(CLOCK_REALTIME, &tmp); |
573 | 0 | ns = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; |
574 | 0 | #endif |
575 | | |
576 | | /* Guarantee the minimal step advancement of the timestamp */ |
577 | 0 | if (previous_ns + SUBMS_MINIMAL_STEP_NS >= ns) |
578 | 0 | ns = previous_ns + SUBMS_MINIMAL_STEP_NS; |
579 | 0 | previous_ns = ns; |
580 | |
|
581 | 0 | return ns; |
582 | 0 | } |
583 | | |
584 | | /* |
585 | | * Generate UUID version 7 per RFC 9562, with the given timestamp. |
586 | | * |
587 | | * UUID version 7 consists of a Unix timestamp in milliseconds (48 bits) and |
588 | | * 74 random bits, excluding the required version and variant bits. To ensure |
589 | | * monotonicity in scenarios of high-frequency UUID generation, we employ the |
590 | | * method "Replace Leftmost Random Bits with Increased Clock Precision (Method 3)", |
591 | | * described in the RFC. This method utilizes 12 bits from the "rand_a" bits |
592 | | * to store a 1/4096 (or 2^12) fraction of sub-millisecond precision. |
593 | | * |
594 | | * unix_ts_ms is a number of milliseconds since start of the UNIX epoch, |
595 | | * and sub_ms is a number of nanoseconds within millisecond. These values are |
596 | | * used for time-dependent bits of UUID. |
597 | | * |
598 | | * NB: all numbers here are unsigned, unix_ts_ms cannot be negative per RFC. |
599 | | */ |
600 | | static pg_uuid_t * |
601 | | generate_uuidv7(uint64 unix_ts_ms, uint32 sub_ms) |
602 | 0 | { |
603 | 0 | pg_uuid_t *uuid = palloc(UUID_LEN); |
604 | 0 | uint32 increased_clock_precision; |
605 | | |
606 | | /* Fill in time part */ |
607 | 0 | uuid->data[0] = (unsigned char) (unix_ts_ms >> 40); |
608 | 0 | uuid->data[1] = (unsigned char) (unix_ts_ms >> 32); |
609 | 0 | uuid->data[2] = (unsigned char) (unix_ts_ms >> 24); |
610 | 0 | uuid->data[3] = (unsigned char) (unix_ts_ms >> 16); |
611 | 0 | uuid->data[4] = (unsigned char) (unix_ts_ms >> 8); |
612 | 0 | uuid->data[5] = (unsigned char) unix_ts_ms; |
613 | | |
614 | | /* |
615 | | * sub-millisecond timestamp fraction (SUBMS_BITS bits, not |
616 | | * SUBMS_MINIMAL_STEP_BITS) |
617 | | */ |
618 | 0 | increased_clock_precision = (sub_ms * (1 << SUBMS_BITS)) / NS_PER_MS; |
619 | | |
620 | | /* Fill the increased clock precision to "rand_a" bits */ |
621 | 0 | uuid->data[6] = (unsigned char) (increased_clock_precision >> 8); |
622 | 0 | uuid->data[7] = (unsigned char) (increased_clock_precision); |
623 | | |
624 | | /* fill everything after the increased clock precision with random bytes */ |
625 | 0 | if (!pg_strong_random(&uuid->data[8], UUID_LEN - 8)) |
626 | 0 | ereport(ERROR, |
627 | 0 | (errcode(ERRCODE_INTERNAL_ERROR), |
628 | 0 | errmsg("could not generate random values"))); |
629 | | |
630 | | #if SUBMS_MINIMAL_STEP_BITS == 10 |
631 | | |
632 | | /* |
633 | | * On systems that have only 10 bits of sub-ms precision, 2 least |
634 | | * significant are dependent on other time-specific bits, and they do not |
635 | | * contribute to uniqueness. To make these bit random we mix in two bits |
636 | | * from CSPRNG. SUBMS_MINIMAL_STEP is chosen so that we still guarantee |
637 | | * monotonicity despite altering these bits. |
638 | | */ |
639 | | uuid->data[7] = uuid->data[7] ^ (uuid->data[8] >> 6); |
640 | | #endif |
641 | | |
642 | | /* |
643 | | * Set magic numbers for a "version 7" (pseudorandom) UUID and variant, |
644 | | * see https://www.rfc-editor.org/rfc/rfc9562#name-version-field |
645 | | */ |
646 | 0 | uuid_set_version(uuid, 7); |
647 | |
|
648 | 0 | return uuid; |
649 | 0 | } |
650 | | |
651 | | /* |
652 | | * Generate UUID version 7 with the current timestamp. |
653 | | */ |
654 | | Datum |
655 | | uuidv7(PG_FUNCTION_ARGS) |
656 | 0 | { |
657 | 0 | int64 ns = get_real_time_ns_ascending(); |
658 | 0 | pg_uuid_t *uuid = generate_uuidv7(ns / NS_PER_MS, ns % NS_PER_MS); |
659 | |
|
660 | 0 | PG_RETURN_UUID_P(uuid); |
661 | 0 | } |
662 | | |
663 | | /* |
664 | | * Similar to uuidv7() but with the timestamp adjusted by the given interval. |
665 | | */ |
666 | | Datum |
667 | | uuidv7_interval(PG_FUNCTION_ARGS) |
668 | 0 | { |
669 | 0 | Interval *shift = PG_GETARG_INTERVAL_P(0); |
670 | 0 | TimestampTz ts; |
671 | 0 | pg_uuid_t *uuid; |
672 | 0 | int64 ns = get_real_time_ns_ascending(); |
673 | 0 | int64 us; |
674 | | |
675 | | /* |
676 | | * Shift the current timestamp by the given interval. To calculate time |
677 | | * shift correctly, we convert the UNIX epoch to TimestampTz and use |
678 | | * timestamptz_pl_interval(). This calculation is done with microsecond |
679 | | * precision. |
680 | | */ |
681 | |
|
682 | 0 | ts = (TimestampTz) (ns / NS_PER_US) - |
683 | 0 | (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * USECS_PER_SEC; |
684 | | |
685 | | /* Compute time shift */ |
686 | 0 | ts = DatumGetTimestampTz(DirectFunctionCall2(timestamptz_pl_interval, |
687 | 0 | TimestampTzGetDatum(ts), |
688 | 0 | IntervalPGetDatum(shift))); |
689 | | |
690 | | /* Convert a TimestampTz value back to an UNIX epoch timestamp */ |
691 | 0 | us = ts + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * USECS_PER_SEC; |
692 | | |
693 | | /* Generate an UUIDv7 */ |
694 | 0 | uuid = generate_uuidv7(us / US_PER_MS, (us % US_PER_MS) * NS_PER_US + ns % NS_PER_US); |
695 | |
|
696 | 0 | PG_RETURN_UUID_P(uuid); |
697 | 0 | } |
698 | | |
699 | | /* |
700 | | * Start of a Gregorian epoch == date2j(1582,10,15) |
701 | | * We cast it to 64-bit because it's used in overflow-prone computations |
702 | | */ |
703 | 0 | #define GREGORIAN_EPOCH_JDATE INT64CONST(2299161) |
704 | | |
705 | | /* |
706 | | * Extract timestamp from UUID. |
707 | | * |
708 | | * Returns null if not RFC 9562 variant or not a version that has a timestamp. |
709 | | */ |
710 | | Datum |
711 | | uuid_extract_timestamp(PG_FUNCTION_ARGS) |
712 | 0 | { |
713 | 0 | pg_uuid_t *uuid = PG_GETARG_UUID_P(0); |
714 | 0 | int version; |
715 | 0 | uint64 tms; |
716 | 0 | TimestampTz ts; |
717 | | |
718 | | /* check if RFC 9562 variant */ |
719 | 0 | if ((uuid->data[8] & 0xc0) != 0x80) |
720 | 0 | PG_RETURN_NULL(); |
721 | | |
722 | 0 | version = uuid->data[6] >> 4; |
723 | |
|
724 | 0 | if (version == 1) |
725 | 0 | { |
726 | 0 | tms = ((uint64) uuid->data[0] << 24) |
727 | 0 | + ((uint64) uuid->data[1] << 16) |
728 | 0 | + ((uint64) uuid->data[2] << 8) |
729 | 0 | + ((uint64) uuid->data[3]) |
730 | 0 | + ((uint64) uuid->data[4] << 40) |
731 | 0 | + ((uint64) uuid->data[5] << 32) |
732 | 0 | + (((uint64) uuid->data[6] & 0xf) << 56) |
733 | 0 | + ((uint64) uuid->data[7] << 48); |
734 | | |
735 | | /* convert 100-ns intervals to us, then adjust */ |
736 | 0 | ts = (TimestampTz) (tms / 10) - |
737 | 0 | ((uint64) POSTGRES_EPOCH_JDATE - GREGORIAN_EPOCH_JDATE) * SECS_PER_DAY * USECS_PER_SEC; |
738 | 0 | PG_RETURN_TIMESTAMPTZ(ts); |
739 | 0 | } |
740 | | |
741 | 0 | if (version == 7) |
742 | 0 | { |
743 | 0 | tms = (uuid->data[5]) |
744 | 0 | + (((uint64) uuid->data[4]) << 8) |
745 | 0 | + (((uint64) uuid->data[3]) << 16) |
746 | 0 | + (((uint64) uuid->data[2]) << 24) |
747 | 0 | + (((uint64) uuid->data[1]) << 32) |
748 | 0 | + (((uint64) uuid->data[0]) << 40); |
749 | | |
750 | | /* convert ms to us, then adjust */ |
751 | 0 | ts = (TimestampTz) (tms * US_PER_MS) - |
752 | 0 | (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * USECS_PER_SEC; |
753 | |
|
754 | 0 | PG_RETURN_TIMESTAMPTZ(ts); |
755 | 0 | } |
756 | | |
757 | | /* not a timestamp-containing UUID version */ |
758 | 0 | PG_RETURN_NULL(); |
759 | 0 | } |
760 | | |
761 | | /* |
762 | | * Extract UUID version. |
763 | | * |
764 | | * Returns null if not RFC 9562 variant. |
765 | | */ |
766 | | Datum |
767 | | uuid_extract_version(PG_FUNCTION_ARGS) |
768 | 0 | { |
769 | 0 | pg_uuid_t *uuid = PG_GETARG_UUID_P(0); |
770 | 0 | uint16 version; |
771 | | |
772 | | /* check if RFC 9562 variant */ |
773 | 0 | if ((uuid->data[8] & 0xc0) != 0x80) |
774 | 0 | PG_RETURN_NULL(); |
775 | | |
776 | 0 | version = uuid->data[6] >> 4; |
777 | |
|
778 | 0 | PG_RETURN_UINT16(version); |
779 | 0 | } |