-
Notifications
You must be signed in to change notification settings - Fork 26
/
pg_uuidv7.c
133 lines (100 loc) · 3.07 KB
/
pg_uuidv7.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "postgres.h"
#include "fmgr.h"
#include "port/pg_bswap.h"
#include "utils/uuid.h"
#include "utils/timestamp.h"
#include <time.h>
/*
* Number of microseconds between unix and postgres epoch
*/
#define EPOCH_DIFF_USECS ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * USECS_PER_DAY)
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(uuid_generate_v7);
Datum uuid_generate_v7(PG_FUNCTION_ARGS)
{
pg_uuid_t *uuid = palloc(UUID_LEN);
struct timespec ts;
uint64_t tms;
/*
* Set first 48 bits to unix epoch timestamp
*/
if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not get CLOCK_REALTIME")));
tms = ((uint64_t)ts.tv_sec * 1000) + ((uint64_t)ts.tv_nsec / 1000000);
tms = pg_hton64(tms << 16);
memcpy(&uuid->data[0], &tms, 6);
if (!pg_strong_random(&uuid->data[6], UUID_LEN - 6))
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not generate random values")));
/*
* Set magic numbers for a "version 7" UUID, see
* https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-00.html#name-uuid-version-7
*/
uuid->data[6] = (uuid->data[6] & 0x0f) | 0x70; /* 4 bit version [0111] */
uuid->data[8] = (uuid->data[8] & 0x3f) | 0x80; /* 2 bit variant [10] */
PG_RETURN_UUID_P(uuid);
}
static uint64_t uuid_v7_to_uint64(pg_uuid_t *uuid)
{
uint64_t ts;
memcpy(&ts, &uuid->data[0], 6);
ts = pg_ntoh64(ts) >> 16;
ts = 1000 * ts - EPOCH_DIFF_USECS;
return ts;
}
PG_FUNCTION_INFO_V1(uuid_v7_to_timestamptz);
Datum uuid_v7_to_timestamptz(PG_FUNCTION_ARGS)
{
pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
uint64_t ts = uuid_v7_to_uint64(uuid);
PG_RETURN_TIMESTAMPTZ(ts);
}
PG_FUNCTION_INFO_V1(uuid_v7_to_timestamp);
Datum uuid_v7_to_timestamp(PG_FUNCTION_ARGS)
{
pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
uint64_t ts = uuid_v7_to_uint64(uuid);
PG_RETURN_TIMESTAMP(ts);
}
static Datum uuid_uint64_to_v7(uint64_t ts, bool zero)
{
pg_uuid_t *uuid = palloc(UUID_LEN);
uint64_t tms;
tms = (ts + EPOCH_DIFF_USECS) / 1000;
tms = pg_hton64(tms << 16);
memcpy(&uuid->data[0], &tms, 6);
if (zero)
memset(&uuid->data[6], 0, UUID_LEN - 6);
else if (!pg_strong_random(&uuid->data[6], UUID_LEN - 6))
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not generate random values")));
/*
* Set magic numbers for a "version 7" UUID, see
* https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-00.html#name-uuid-version-7
*/
uuid->data[6] = (uuid->data[6] & 0x0f) | 0x70; /* 4 bit version [0111] */
uuid->data[8] = (uuid->data[8] & 0x3f) | 0x80; /* 2 bit variant [10] */
PG_RETURN_UUID_P(uuid);
}
PG_FUNCTION_INFO_V1(uuid_timestamptz_to_v7);
Datum uuid_timestamptz_to_v7(PG_FUNCTION_ARGS)
{
TimestampTz ts = PG_GETARG_TIMESTAMPTZ(0);
bool zero = false;
if (!PG_ARGISNULL(1))
zero = PG_GETARG_BOOL(1);
return uuid_uint64_to_v7(ts, zero);
}
PG_FUNCTION_INFO_V1(uuid_timestamp_to_v7);
Datum uuid_timestamp_to_v7(PG_FUNCTION_ARGS)
{
Timestamp ts = PG_GETARG_TIMESTAMP(0);
bool zero = false;
if (!PG_ARGISNULL(1))
zero = PG_GETARG_BOOL(1);
return uuid_uint64_to_v7(ts, zero);
}