-- Local Issue Tracking Database Schema -- SQLite schema for local issue storage with full referential integrity -- Enable foreign key constraints PRAGMA foreign_keys = ON; -- Issues table - core issue data CREATE TABLE IF NOT EXISTS issues ( id TEXT PRIMARY KEY, number INTEGER UNIQUE NOT NULL, title TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', state TEXT NOT NULL CHECK (state IN ('open', 'closed', 'in_progress', 'blocked')), created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, closed_at TIMESTAMP NULL, milestone_id TEXT, backend_id TEXT, backend_type TEXT DEFAULT 'local', sync_metadata TEXT, -- JSON for sync data FOREIGN KEY (milestone_id) REFERENCES milestones(id) ON DELETE SET NULL ); -- Create index for issue number lookups CREATE INDEX IF NOT EXISTS idx_issues_number ON issues(number); CREATE INDEX IF NOT EXISTS idx_issues_state ON issues(state); CREATE INDEX IF NOT EXISTS idx_issues_updated_at ON issues(updated_at); CREATE INDEX IF NOT EXISTS idx_issues_backend_id ON issues(backend_id); -- Labels table CREATE TABLE IF NOT EXISTS labels ( id TEXT PRIMARY KEY, name TEXT UNIQUE NOT NULL, color TEXT, description TEXT, backend_id TEXT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- Create index for label name lookups CREATE INDEX IF NOT EXISTS idx_labels_name ON labels(name); -- Issue-Label many-to-many relationship CREATE TABLE IF NOT EXISTS issue_labels ( issue_id TEXT NOT NULL, label_id TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (issue_id, label_id), FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE, FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE CASCADE ); -- Users table CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, username TEXT UNIQUE NOT NULL, display_name TEXT, email TEXT, avatar_url TEXT, backend_id TEXT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- Create index for username lookups CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); -- Issue-User assignment many-to-many relationship CREATE TABLE IF NOT EXISTS issue_assignees ( issue_id TEXT NOT NULL, user_id TEXT NOT NULL, assigned_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (issue_id, user_id), FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); -- Milestones table CREATE TABLE IF NOT EXISTS milestones ( id TEXT PRIMARY KEY, title TEXT NOT NULL, description TEXT, state TEXT NOT NULL DEFAULT 'open' CHECK (state IN ('open', 'closed')), due_date TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, backend_id TEXT ); -- Create index for milestone title lookups CREATE INDEX IF NOT EXISTS idx_milestones_title ON milestones(title); -- Comments table CREATE TABLE IF NOT EXISTS comments ( id TEXT PRIMARY KEY, issue_id TEXT NOT NULL, author_id TEXT NOT NULL, body TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP, backend_id TEXT, FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE, FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE ); -- Create index for comment lookups CREATE INDEX IF NOT EXISTS idx_comments_issue_id ON comments(issue_id); CREATE INDEX IF NOT EXISTS idx_comments_created_at ON comments(created_at); -- Sync tracking table CREATE TABLE IF NOT EXISTS sync_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, backend_type TEXT NOT NULL, sync_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, success BOOLEAN NOT NULL, issues_synced INTEGER DEFAULT 0, errors_count INTEGER DEFAULT 0, details TEXT -- JSON for sync details ); -- Configuration table for backend settings CREATE TABLE IF NOT EXISTS config ( key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- Triggers to automatically update updated_at timestamps CREATE TRIGGER IF NOT EXISTS update_issues_timestamp AFTER UPDATE ON issues BEGIN UPDATE issues SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END; CREATE TRIGGER IF NOT EXISTS update_milestones_timestamp AFTER UPDATE ON milestones BEGIN UPDATE milestones SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END; -- Views for common queries CREATE VIEW IF NOT EXISTS issue_summary AS SELECT i.id, i.number, i.title, i.state, i.created_at, i.updated_at, i.closed_at, m.title as milestone_title, COUNT(c.id) as comment_count, GROUP_CONCAT(l.name) as labels, GROUP_CONCAT(u.username) as assignees FROM issues i LEFT JOIN milestones m ON i.milestone_id = m.id LEFT JOIN comments c ON i.id = c.issue_id LEFT JOIN issue_labels il ON i.id = il.issue_id LEFT JOIN labels l ON il.label_id = l.id LEFT JOIN issue_assignees ia ON i.id = ia.issue_id LEFT JOIN users u ON ia.user_id = u.id GROUP BY i.id, i.number, i.title, i.state, i.created_at, i.updated_at, i.closed_at, m.title; -- Full-text search setup (optional - disabled for now due to compatibility issues) -- Can be enabled later by creating FTS5 virtual table manually -- CREATE VIRTUAL TABLE IF NOT EXISTS issue_search USING fts5( -- issue_id UNINDEXED, -- title, -- description -- );