generated from coulomb/repo-seed
Updated by fix-consistency on 2026-05-17: - update .custodian-brief.md for issue-core
169 lines
5.6 KiB
SQL
169 lines
5.6 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 (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
|
|
-- ); |