diff --git a/migrations/006_merge_requests.sql b/migrations/006_merge_requests.sql new file mode 100644 index 0000000..f579189 --- /dev/null +++ b/migrations/006_merge_requests.sql @@ -0,0 +1,97 @@ +-- Migration 006: Merge Requests, MR Labels, Assignees, Reviewers +-- Schema version: 6 +-- Adds CP2 MR ingestion support + +-- Merge requests table +CREATE TABLE merge_requests ( + id INTEGER PRIMARY KEY, + gitlab_id INTEGER UNIQUE NOT NULL, + project_id INTEGER NOT NULL REFERENCES projects(id), + iid INTEGER NOT NULL, + title TEXT, + description TEXT, + state TEXT, -- 'opened' | 'merged' | 'closed' | 'locked' + draft INTEGER NOT NULL DEFAULT 0, -- 0/1 (SQLite boolean) - work-in-progress status + author_username TEXT, + source_branch TEXT, + target_branch TEXT, + head_sha TEXT, -- Current commit SHA at head of source branch (CP3-ready) + references_short TEXT, -- Short reference e.g. "!123" (CP3-ready for display) + references_full TEXT, -- Full reference e.g. "group/project!123" (CP3-ready for cross-project) + detailed_merge_status TEXT, -- preferred, non-deprecated (replaces merge_status) + merge_user_username TEXT, -- preferred over deprecated merged_by + created_at INTEGER, -- ms epoch UTC + updated_at INTEGER, -- ms epoch UTC + merged_at INTEGER, -- ms epoch UTC (NULL if not merged) + closed_at INTEGER, -- ms epoch UTC (NULL if not closed) + last_seen_at INTEGER NOT NULL, -- ms epoch UTC, updated on every upsert + -- Prevents re-fetching discussions on cursor rewind / reruns unless MR changed. + discussions_synced_for_updated_at INTEGER, + -- Sync health telemetry for debuggability + discussions_sync_last_attempt_at INTEGER, -- ms epoch UTC of last sync attempt + discussions_sync_attempts INTEGER DEFAULT 0, -- count of sync attempts for this MR version + discussions_sync_last_error TEXT, -- last error message if sync failed + web_url TEXT, + raw_payload_id INTEGER REFERENCES raw_payloads(id) +); + +CREATE INDEX idx_mrs_project_updated ON merge_requests(project_id, updated_at); +CREATE INDEX idx_mrs_author ON merge_requests(author_username); +CREATE INDEX idx_mrs_target_branch ON merge_requests(project_id, target_branch); +CREATE INDEX idx_mrs_source_branch ON merge_requests(project_id, source_branch); +CREATE INDEX idx_mrs_state ON merge_requests(project_id, state); +CREATE INDEX idx_mrs_detailed_merge_status ON merge_requests(project_id, detailed_merge_status); +CREATE INDEX idx_mrs_draft ON merge_requests(project_id, draft); +CREATE INDEX idx_mrs_discussions_sync ON merge_requests(project_id, discussions_synced_for_updated_at); +CREATE UNIQUE INDEX uq_mrs_project_iid ON merge_requests(project_id, iid); + +-- MR-Label junction (reuses labels table from CP1) +CREATE TABLE mr_labels ( + merge_request_id INTEGER REFERENCES merge_requests(id) ON DELETE CASCADE, + label_id INTEGER REFERENCES labels(id) ON DELETE CASCADE, + PRIMARY KEY(merge_request_id, label_id) +); +CREATE INDEX idx_mr_labels_label ON mr_labels(label_id); + +-- MR assignees (same pattern as issue_assignees) +CREATE TABLE mr_assignees ( + merge_request_id INTEGER REFERENCES merge_requests(id) ON DELETE CASCADE, + username TEXT NOT NULL, + PRIMARY KEY(merge_request_id, username) +); +CREATE INDEX idx_mr_assignees_username ON mr_assignees(username); + +-- MR reviewers (MR-specific, not applicable to issues) +CREATE TABLE mr_reviewers ( + merge_request_id INTEGER REFERENCES merge_requests(id) ON DELETE CASCADE, + username TEXT NOT NULL, + PRIMARY KEY(merge_request_id, username) +); +CREATE INDEX idx_mr_reviewers_username ON mr_reviewers(username); + +-- Add FK constraint to discussions table for merge_request_id +-- Note: SQLite doesn't support ADD CONSTRAINT, the FK was defined in CP1 but nullable +-- We just need to add an index if not already present +CREATE INDEX IF NOT EXISTS idx_discussions_mr_id ON discussions(merge_request_id); +CREATE INDEX IF NOT EXISTS idx_discussions_mr_resolved ON discussions(merge_request_id, resolved, resolvable); + +-- Additional indexes for DiffNote queries (notes table from CP1) +-- These composite indexes enable efficient file-context queries for CP3 +CREATE INDEX IF NOT EXISTS idx_notes_type ON notes(note_type); +CREATE INDEX IF NOT EXISTS idx_notes_new_path ON notes(position_new_path); +CREATE INDEX IF NOT EXISTS idx_notes_new_path_line ON notes(position_new_path, position_new_line); +CREATE INDEX IF NOT EXISTS idx_notes_old_path_line ON notes(position_old_path, position_old_line); + +-- CP2: capture richer diff note position shapes (minimal, still MVP) +-- These fields support modern GitLab diff note semantics without full diff reconstruction +ALTER TABLE notes ADD COLUMN position_type TEXT; -- 'text' | 'image' | 'file' +ALTER TABLE notes ADD COLUMN position_line_range_start INTEGER; -- multi-line comment start +ALTER TABLE notes ADD COLUMN position_line_range_end INTEGER; -- multi-line comment end +-- DiffNote SHA triplet for commit context (CP3-ready, zero extra API cost) +ALTER TABLE notes ADD COLUMN position_base_sha TEXT; -- Base commit SHA for diff +ALTER TABLE notes ADD COLUMN position_start_sha TEXT; -- Start commit SHA for diff +ALTER TABLE notes ADD COLUMN position_head_sha TEXT; -- Head commit SHA for diff + +-- Update schema version +INSERT INTO schema_version (version, applied_at, description) +VALUES (6, strftime('%s', 'now') * 1000, 'Merge requests, MR labels, assignees, reviewers');