generated from coulomb/repo-seed
189 lines
6.1 KiB
SQL
189 lines
6.1 KiB
SQL
-- 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 (if SQLite supports FTS)
|
|
CREATE VIRTUAL TABLE IF NOT EXISTS issue_search USING fts5(
|
|
issue_id,
|
|
title,
|
|
description,
|
|
labels,
|
|
content='issues'
|
|
);
|
|
|
|
-- Trigger to keep FTS index updated
|
|
CREATE TRIGGER IF NOT EXISTS issue_search_insert AFTER INSERT ON issues
|
|
BEGIN
|
|
INSERT INTO issue_search(issue_id, title, description)
|
|
VALUES (NEW.id, NEW.title, NEW.description);
|
|
END;
|
|
|
|
CREATE TRIGGER IF NOT EXISTS issue_search_update AFTER UPDATE ON issues
|
|
BEGIN
|
|
UPDATE issue_search
|
|
SET title = NEW.title, description = NEW.description
|
|
WHERE issue_id = NEW.id;
|
|
END;
|
|
|
|
CREATE TRIGGER IF NOT EXISTS issue_search_delete AFTER DELETE ON issues
|
|
BEGIN
|
|
DELETE FROM issue_search WHERE issue_id = OLD.id;
|
|
END; |