Skip to content

Instantly share code, notes, and snippets.

@fabiolimace
Last active February 14, 2025 17:46
Show Gist options
  • Save fabiolimace/e3c3d354d1afe0b3175f65be2d962523 to your computer and use it in GitHub Desktop.
Save fabiolimace/e3c3d354d1afe0b3175f65be2d962523 to your computer and use it in GitHub Desktop.
UUIDv7 for SQLite using view and triggers
DROP VIEW IF EXISTS uuid7;
CREATE VIEW uuid7 AS
WITH unixtime AS (
SELECT CAST((STRFTIME('%s') * 1000) + ((STRFTIME('%f') * 1000) % 1000) AS INTEGER) AS time
-- SELECT CAST((UNIXEPOCH('subsec') * 1000) AS INTEGER) AS time -- for SQLite v3.38.0 (2022)
)
SELECT PRINTF('%08x-%04x-%04x-%04x-%012x',
(select time from unixtime) >> 16,
(select time from unixtime) & 0xffff,
ABS(RANDOM()) % 0x0fff + 0x7000,
ABS(RANDOM()) % 0x3fff + 0x8000,
ABS(RANDOM()) >> 16) AS next;
-- EXAMPLE:
--
-- sqlite> SELECT next FROM uuid7;
-- 01901973-f202-71ca-9a22-14e7146dab85
--
-- DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table (
uuid TEXT PRIMARY KEY,
created DATE NOT NULL,
content TEXT NOT NULL,
CONSTRAINT check_my_table_uuid CHECK (uuid REGEXP '[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}')
);
DROP TRIGGER IF EXISTS trigger_after_insert_on_my_table;
CREATE TRIGGER trigger_after_insert_on_my_table
AFTER INSERT ON my_table FOR EACH ROW WHEN NEW.uuid IS NULL
BEGIN
UPDATE my_table SET uuid = (SELECT next FROM uuid7) WHERE ROWID = NEW.ROWID;
END;
-- NOTES:
-- 1. The trigger generates a UUIDv7 when none is provided in the insert statement.
-- 2. The check constraint verifies if the provided UUID is valid on updates and inserts.
-- 3. Unfortunately, we can't use a sub-query as DEFAULT value for the PK. That's why we use the trigger.
-- Insert some lines using the trigger
INSERT INTO my_table VALUES(NULL, DATETIME(), 'Hello, World!');
INSERT INTO my_table VALUES(NULL, DATETIME(), 'Hello, World!');
INSERT INTO my_table VALUES(NULL, DATETIME(), 'Hello, World!');
-- Insert more lines using the view (ignoring the trigger)
INSERT INTO my_table VALUES((SELECT next FROM uuid7), DATETIME(), 'Hello, World!');
INSERT INTO my_table VALUES((SELECT next FROM uuid7), DATETIME(), 'Hello, World!');
INSERT INTO my_table VALUES((SELECT next FROM uuid7), DATETIME(), 'Hello, World!');
-- Insert more lines providing UUIDs (also ignoring the trigger)
INSERT INTO my_table VALUES('99f30c47-d5be-4803-9f2b-95c232f89877', DATETIME(), 'Hello, World!');
INSERT INTO my_table VALUES('d38ae560-321f-408c-a70b-a5ad8f27d346', DATETIME(), 'Hello, World!');
INSERT INTO my_table VALUES('c78a27ec-b065-4af1-9619-7a676d34275a', DATETIME(), 'Hello, World!');
-- Try to insert lines with invalid UUIDs; they must throw check error
INSERT INTO my_table VALUES('', DATETIME(), 'Hello, World!');
INSERT INTO my_table VALUES('asdfasdfasdf', DATETIME(), 'Hello, World!');
INSERT INTO my_table VALUES('0d56252d-51a6-4d1b-8ddc----------', DATETIME(), 'Hello, World!');
SELECT * FROM my_table;
-- 01901a7a-3f0b-714e-b696-6d48d23a262e|2024-06-15 05:58:57|Hello, World!
-- 01901a7a-3f0b-76fb-82cd-17b01c4e7cfe|2024-06-15 05:58:57|Hello, World!
-- 01901a7a-3f0b-7a42-a03b-3055c708f422|2024-06-15 05:58:57|Hello, World!
-- 01901a7a-3f0b-7ad1-b2b9-0d4ead036759|2024-06-15 05:58:57|Hello, World!
-- 01901a7a-3f0b-7076-ae04-693bd8f276aa|2024-06-15 05:58:57|Hello, World!
-- 01901a7a-3f0c-7e05-bae2-4792b2e3192b|2024-06-15 05:58:57|Hello, World!
-- 99f30c47-d5be-4803-9f2b-95c232f89877|2024-06-15 05:58:57|Hello, World!
-- d38ae560-321f-408c-a70b-a5ad8f27d346|2024-06-15 05:58:57|Hello, World!
-- c78a27ec-b065-4af1-9619-7a676d34275a|2024-06-15 05:58:57|Hello, World!
@brettinternet
Copy link

Hi @fabiolimace, I see now. Thanks for sharing this and explaining your solution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment