fix(db): add AUTOINCREMENT to prevent SQLite ROWID reuse

Without AUTOINCREMENT, SQLite can reassign the highest deleted ROWID to
a new row. This is a data integrity bug: a deleted entity's ID could be
assigned to a completely different entity, breaking bookmarks, API
caches, and URL stability.
This commit is contained in:
Jon Seager 2026-02-06 10:09:13 +00:00
parent d9d87ef849
commit 97c46d034b
No known key found for this signature in database
4 changed files with 14 additions and 14 deletions

View file

@ -1,14 +1,14 @@
-- Authentication and authorisation tables -- Authentication and authorisation tables
CREATE TABLE users ( CREATE TABLE users (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE, username TEXT NOT NULL UNIQUE,
uuid TEXT, uuid TEXT,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
); );
CREATE TABLE tokens ( CREATE TABLE tokens (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token_hash TEXT NOT NULL UNIQUE, token_hash TEXT NOT NULL UNIQUE,
name TEXT NOT NULL, name TEXT NOT NULL,
@ -20,7 +20,7 @@ CREATE INDEX idx_tokens_user_id ON tokens(user_id);
CREATE INDEX idx_tokens_token_hash ON tokens(token_hash); CREATE INDEX idx_tokens_token_hash ON tokens(token_hash);
CREATE TABLE sessions ( CREATE TABLE sessions (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
session_token_hash TEXT NOT NULL, session_token_hash TEXT NOT NULL,
created_at TEXT NOT NULL, created_at TEXT NOT NULL,
@ -31,7 +31,7 @@ CREATE INDEX idx_sessions_user_id ON sessions(user_id);
CREATE INDEX idx_sessions_expires_at ON sessions(expires_at); CREATE INDEX idx_sessions_expires_at ON sessions(expires_at);
CREATE TABLE passkey_credentials ( CREATE TABLE passkey_credentials (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
credential_json TEXT NOT NULL, credential_json TEXT NOT NULL,
name TEXT NOT NULL DEFAULT 'default', name TEXT NOT NULL DEFAULT 'default',
@ -41,7 +41,7 @@ CREATE TABLE passkey_credentials (
CREATE INDEX idx_passkey_credentials_user_id ON passkey_credentials(user_id); CREATE INDEX idx_passkey_credentials_user_id ON passkey_credentials(user_id);
CREATE TABLE registration_tokens ( CREATE TABLE registration_tokens (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
token_hash TEXT NOT NULL UNIQUE, token_hash TEXT NOT NULL UNIQUE,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
expires_at TEXT NOT NULL, expires_at TEXT NOT NULL,

View file

@ -1,7 +1,7 @@
-- Coffee supply chain and equipment tables -- Coffee supply chain and equipment tables
CREATE TABLE roasters ( CREATE TABLE roasters (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, name TEXT NOT NULL,
country TEXT NOT NULL, country TEXT NOT NULL,
city TEXT, city TEXT,
@ -12,7 +12,7 @@ CREATE TABLE roasters (
CREATE UNIQUE INDEX idx_roasters_slug ON roasters(slug); CREATE UNIQUE INDEX idx_roasters_slug ON roasters(slug);
CREATE TABLE roasts ( CREATE TABLE roasts (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
roaster_id INTEGER NOT NULL REFERENCES roasters(id) ON DELETE CASCADE, roaster_id INTEGER NOT NULL REFERENCES roasters(id) ON DELETE CASCADE,
name TEXT NOT NULL, name TEXT NOT NULL,
origin TEXT, origin TEXT,
@ -27,7 +27,7 @@ CREATE INDEX idx_roasts_roaster_id ON roasts(roaster_id);
CREATE UNIQUE INDEX idx_roasts_roaster_slug ON roasts(roaster_id, slug); CREATE UNIQUE INDEX idx_roasts_roaster_slug ON roasts(roaster_id, slug);
CREATE TABLE bags ( CREATE TABLE bags (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
roast_id INTEGER NOT NULL REFERENCES roasts(id) ON DELETE CASCADE, roast_id INTEGER NOT NULL REFERENCES roasts(id) ON DELETE CASCADE,
roast_date TEXT, roast_date TEXT,
amount REAL NOT NULL, amount REAL NOT NULL,
@ -41,7 +41,7 @@ CREATE INDEX idx_bags_roast_id ON bags(roast_id);
CREATE INDEX idx_bags_closed ON bags(closed); CREATE INDEX idx_bags_closed ON bags(closed);
CREATE TABLE gear ( CREATE TABLE gear (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
category TEXT NOT NULL CHECK (category IN ('grinder', 'brewer', 'filter_paper')), category TEXT NOT NULL CHECK (category IN ('grinder', 'brewer', 'filter_paper')),
make TEXT NOT NULL, make TEXT NOT NULL,
model TEXT NOT NULL, model TEXT NOT NULL,

View file

@ -1,7 +1,7 @@
-- Brew, cafe, and cup tables -- Brew, cafe, and cup tables
CREATE TABLE brews ( CREATE TABLE brews (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
bag_id INTEGER NOT NULL REFERENCES bags(id) ON DELETE CASCADE, bag_id INTEGER NOT NULL REFERENCES bags(id) ON DELETE CASCADE,
coffee_weight REAL NOT NULL, coffee_weight REAL NOT NULL,
grinder_id INTEGER NOT NULL REFERENCES gear(id) ON DELETE RESTRICT, grinder_id INTEGER NOT NULL REFERENCES gear(id) ON DELETE RESTRICT,
@ -18,7 +18,7 @@ CREATE INDEX idx_brews_bag_id ON brews(bag_id);
CREATE INDEX idx_brews_created_at ON brews(created_at DESC); CREATE INDEX idx_brews_created_at ON brews(created_at DESC);
CREATE TABLE cafes ( CREATE TABLE cafes (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, name TEXT NOT NULL,
slug TEXT NOT NULL, slug TEXT NOT NULL,
city TEXT NOT NULL, city TEXT NOT NULL,
@ -32,7 +32,7 @@ CREATE TABLE cafes (
CREATE UNIQUE INDEX idx_cafes_slug ON cafes(slug); CREATE UNIQUE INDEX idx_cafes_slug ON cafes(slug);
CREATE TABLE cups ( CREATE TABLE cups (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
roast_id INTEGER NOT NULL REFERENCES roasts(id) ON DELETE RESTRICT, roast_id INTEGER NOT NULL REFERENCES roasts(id) ON DELETE RESTRICT,
cafe_id INTEGER NOT NULL REFERENCES cafes(id) ON DELETE RESTRICT, cafe_id INTEGER NOT NULL REFERENCES cafes(id) ON DELETE RESTRICT,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),

View file

@ -1,7 +1,7 @@
-- Timeline events and AI usage tracking -- Timeline events and AI usage tracking
CREATE TABLE timeline_events ( CREATE TABLE timeline_events (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
entity_type TEXT NOT NULL CHECK (entity_type IN ('roaster', 'roast', 'bag', 'gear', 'brew', 'cafe', 'cup')), entity_type TEXT NOT NULL CHECK (entity_type IN ('roaster', 'roast', 'bag', 'gear', 'brew', 'cafe', 'cup')),
entity_id INTEGER NOT NULL, entity_id INTEGER NOT NULL,
action TEXT NOT NULL, action TEXT NOT NULL,
@ -17,7 +17,7 @@ CREATE INDEX idx_timeline_events_entity ON timeline_events(entity_type, entity_i
CREATE INDEX idx_timeline_events_occurred_at ON timeline_events(occurred_at DESC); CREATE INDEX idx_timeline_events_occurred_at ON timeline_events(occurred_at DESC);
CREATE TABLE ai_usage ( CREATE TABLE ai_usage (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
model TEXT NOT NULL, model TEXT NOT NULL,
endpoint TEXT NOT NULL, endpoint TEXT NOT NULL,