以前書いた記事でPostgreSQLでULID likeなUUIDを生成する関数を作成した。似たような生成を行うことができるUUID v7がいつの間にか定義されたのでPostgreSQLのファンクションを作成してみた。
1 create OR REPLACE function gen_uuid_v7() returns uuid as $$
2 select substring(
3 (
4 substring(
5 decode(
6 to_hex(
7 -- b'0100000000000000000000000000000000000000000000000000000000000000'::bigint -- to_hexしたときに上位桁が0だと破棄される。必要な桁まで削除されてしまうのを防ぐ。
8 int8'4611686018427387904' + (
9 (
10 extract(epoch from now()) -- create epoch time
11 * 1000 -- ミリ秒化_
12 )::bigint::bit(64) -- マイクロ秒を切り捨て
13 & int8'281474976710655'::bit(64) -- 下位48bitのみを残すため。10889年まで枯渇しないので、今のところなくても良い
14 )::bigint
15 )
16 , 'hex') -- byteaで扱う
17 , 3) -- 上位16bitを削除
18 -- decode(to_hex(('101010101111111100000000'::bit(24))::bigint), 'hex') AS bytea_result;
19
20 -- randomの32bitの生成とversion/variantの追加
21 || decode(to_hex(((('x' || encode(gen_random_bytes(4),'hex'))::bit(32) | '01110000000000001000000000000000'::bit(32)) & '01111111111111111011111111111111'::bit(32))::bigint), 'hex')
22 -- randomの下位48bitの生成
23 || gen_random_bytes(6)
24 )::text
25 , 3)::uuid; -- bytea -> textしたときの\xを削除しuuidにキャスト
26 $$ language sql;
27
28 \timing
29 SELECT count(*) FROM (SELECT gen_uuid_v7() from generate_series(1, 100000)) t;
30 SELECT count(*) FROM (SELECT gen_random_uuid() from generate_series(1, 100000)) t;
31 SELECT count(*) FROM (SELECT gen_random_bytes(16) from generate_series(1, 100000)) t;
この関数はちょっと遅い。
またPostgreSQL Conference 2025のLTにあったとおり、次期バージョンでUUIDv7を生成する関数が入るらしい。