|
CREATE EXTENSION IF NOT EXISTS pgcrypto; |
|
DROP DOMAIN IF EXISTS ulid CASCADE; |
|
CREATE DOMAIN ulid AS bytea; |
|
|
|
|
|
CREATE OR REPLACE FUNCTION generate_ulid() |
|
RETURNS ulid |
|
AS |
|
$$ |
|
-- concatenate(6 timestamp bytes, 10 entropy bytes) |
|
SELECT DECODE(LPAD(TO_HEX((EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT), 12, '0'), 'hex') || gen_random_bytes(10); |
|
$$ |
|
LANGUAGE sql |
|
VOLATILE; |
|
|
|
CREATE OR REPLACE FUNCTION ulid_to_base32(id ulid) RETURNS TEXT AS |
|
$$ |
|
DECLARE |
|
encoding BYTEA = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; |
|
output TEXT = ''; |
|
ulid BYTEA = id; |
|
BEGIN |
|
-- Encode the timestamp |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 0) & 224) >> 5)); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 0) & 31))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 1) & 248) >> 3)); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 1) & 7) << 2) | ((GET_BYTE(ulid, 2) & 192) >> 6))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 2) & 62) >> 1)); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 2) & 1) << 4) | ((GET_BYTE(ulid, 3) & 240) >> 4))); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 3) & 15) << 1) | ((GET_BYTE(ulid, 4) & 128) >> 7))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 4) & 124) >> 2)); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 4) & 3) << 3) | ((GET_BYTE(ulid, 5) & 224) >> 5))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 5) & 31))); |
|
|
|
-- Encode the entropy |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 6) & 248) >> 3)); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 6) & 7) << 2) | ((GET_BYTE(ulid, 7) & 192) >> 6))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 7) & 62) >> 1)); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 7) & 1) << 4) | ((GET_BYTE(ulid, 8) & 240) >> 4))); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 8) & 15) << 1) | ((GET_BYTE(ulid, 9) & 128) >> 7))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 9) & 124) >> 2)); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 9) & 3) << 3) | ((GET_BYTE(ulid, 10) & 224) >> 5))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 10) & 31))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 11) & 248) >> 3)); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 11) & 7) << 2) | ((GET_BYTE(ulid, 12) & 192) >> 6))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 12) & 62) >> 1)); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 12) & 1) << 4) | ((GET_BYTE(ulid, 13) & 240) >> 4))); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 13) & 15) << 1) | ((GET_BYTE(ulid, 14) & 128) >> 7))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 14) & 124) >> 2)); |
|
output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 14) & 3) << 3) | ((GET_BYTE(ulid, 15) & 224) >> 5))); |
|
output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 15) & 31))); |
|
|
|
RETURN output; |
|
END |
|
$$ |
|
LANGUAGE plpgsql |
|
VOLATILE; |
|
|
|
CREATE CAST (ulid AS bytea) |
|
WITH INOUT |
|
AS IMPLICIT; |
|
|
|
CREATE CAST (ulid AS TEXT) |
|
WITH FUNCTION ulid_to_base32 |
|
AS IMPLICIT; |