pub(crate) const VERSION: i64 = 11;
pub(crate) const SCHEMA: &str = r#"
CREATE TABLE repositories (
id INTEGER PRIMARY KEY,
identity TEXT UNIQUE NOT NULL,
default_branch TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE checkouts (
id INTEGER PRIMARY KEY,
repository_id INTEGER NOT NULL REFERENCES repositories(id),
root_path TEXT NOT NULL UNIQUE,
current_branch TEXT
);
CREATE TABLE files (
id INTEGER PRIMARY KEY,
repository_id INTEGER NOT NULL REFERENCES repositories(id),
path TEXT NOT NULL,
language TEXT,
mtime INTEGER, -- last-modified time, unix *nanoseconds*
-- (git-style racy-edit protection)
git_ts INTEGER, -- last git commit time touching this file
content_hash TEXT,
indexed_at INTEGER,
UNIQUE(repository_id, path)
);
CREATE TABLE symbols (
id INTEGER PRIMARY KEY,
repository_id INTEGER NOT NULL REFERENCES repositories(id),
file_id INTEGER NOT NULL REFERENCES files(id),
name TEXT NOT NULL,
name_lower TEXT NOT NULL,
kind TEXT NOT NULL,
language TEXT NOT NULL,
line INTEGER NOT NULL,
end_line INTEGER, -- 1-based last line of the definition body
parent TEXT,
visibility TEXT -- public|crate|private|protected; NULL when
-- unknown (pre-v9 rows backfill lazily)
);
CREATE INDEX idx_symbols_name_lower ON symbols(name_lower);
CREATE INDEX idx_symbols_file ON symbols(file_id);
CREATE INDEX idx_symbols_repo ON symbols(repository_id);
-- fuzzy candidate narrowing: trigram FTS over symbol names
CREATE VIRTUAL TABLE symbols_fts USING fts5(
name,
content='symbols',
content_rowid='id',
tokenize='trigram'
);
-- keep the external-content FTS index in sync with symbols
-- (the AFTER INSERT trigger is FTS_INSERT_TRIGGER, defined once below)
CREATE TRIGGER symbols_ad AFTER DELETE ON symbols BEGIN
INSERT INTO symbols_fts(symbols_fts, rowid, name) VALUES ('delete', old.id, old.name);
END;
CREATE TRIGGER symbols_au AFTER UPDATE ON symbols BEGIN
INSERT INTO symbols_fts(symbols_fts, rowid, name) VALUES ('delete', old.id, old.name);
INSERT INTO symbols_fts(rowid, name) VALUES (new.id, new.name);
END;
CREATE TABLE coverage (
id INTEGER PRIMARY KEY,
repository_id INTEGER NOT NULL REFERENCES repositories(id),
scope TEXT NOT NULL DEFAULT 'full',
files_seen INTEGER,
files_indexed INTEGER,
status TEXT NOT NULL,
last_indexed_at INTEGER,
UNIQUE(repository_id, scope)
);
-- raw, append-only interaction log
CREATE TABLE events (
id INTEGER PRIMARY KEY,
type TEXT NOT NULL, -- search | open | select
query TEXT, -- normalized query, when applicable
repository_id INTEGER,
path TEXT, -- repo-relative file, for open/select
line INTEGER,
branch TEXT,
ts INTEGER NOT NULL,
source TEXT, -- caller label, for search rows
results INTEGER, -- hits returned; 0 = a miss
flags TEXT, -- canonical flag set, comma-joined
status TEXT, -- hit | miss (absent) | warming (not ready)
coverage TEXT -- index state on arrival: complete|warming|none
);
CREATE INDEX idx_events_repo ON events(repository_id, id);
-- usage counters. Separate from `events` because that log is pruned to a
-- rolling window, which makes it a ceiling rather than a count; these rows are
-- the only cumulative record of how rq is actually used. Read by `--usage`,
-- never by ranking.
CREATE TABLE usage_daily (
day TEXT NOT NULL, -- local date, YYYY-MM-DD
source TEXT NOT NULL,
flags TEXT NOT NULL,
searches INTEGER NOT NULL,
misses INTEGER NOT NULL, -- answered nothing, against a ready index
warming INTEGER NOT NULL, -- answered nothing because it wasn't ready
on_complete INTEGER NOT NULL, -- ran against a fully indexed repo
PRIMARY KEY (day, source, flags)
);
-- rollup the ranking hot path reads. Keyed by (file, name) rather than
-- symbol_id so learning survives reindexing (symbol ids are recreated on every
-- file re-extract; file+name is stable).
CREATE TABLE selection_stats (
repository_id INTEGER NOT NULL,
query_norm TEXT NOT NULL,
file TEXT NOT NULL,
name TEXT NOT NULL,
selections INTEGER NOT NULL,
last_selected_at INTEGER,
PRIMARY KEY (repository_id, query_norm, file, name)
);
-- small key/value store (e.g. the event-rollup high-water mark)
CREATE TABLE meta (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
"#;
pub(crate) const MIGRATION_V2: &str = r#"
DROP TABLE IF EXISTS selection_stats;
DROP TABLE IF EXISTS events;
CREATE TABLE events (
id INTEGER PRIMARY KEY,
type TEXT NOT NULL,
query TEXT,
repository_id INTEGER,
path TEXT,
line INTEGER,
branch TEXT,
ts INTEGER NOT NULL
);
CREATE TABLE selection_stats (
repository_id INTEGER NOT NULL,
query_norm TEXT NOT NULL,
file TEXT NOT NULL,
name TEXT NOT NULL,
selections INTEGER NOT NULL,
last_selected_at INTEGER,
PRIMARY KEY (repository_id, query_norm, file, name)
);
CREATE TABLE IF NOT EXISTS meta (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
"#;
pub(crate) const MIGRATION_V3: &str = r#"
ALTER TABLE files ADD COLUMN git_ts INTEGER;
"#;
pub(crate) const MIGRATION_V4: &str = r#"
ALTER TABLE symbols ADD COLUMN end_line INTEGER;
"#;
pub(crate) const MIGRATION_V5: &str = r#"
CREATE INDEX IF NOT EXISTS idx_symbols_repo ON symbols(repository_id);
CREATE INDEX IF NOT EXISTS idx_events_repo ON events(repository_id, id);
"#;
pub(crate) const MIGRATION_V6: &str = r#"
UPDATE files SET mtime = mtime * 1000000000
WHERE mtime IS NOT NULL AND mtime < 100000000000;
"#;
pub(crate) const MIGRATION_V7: &str = r#"
ALTER TABLE repositories DROP COLUMN display_name;
"#;
pub(crate) const MIGRATION_V8: &str = r#"
UPDATE coverage SET status = 'warming' WHERE status = 'partial';
"#;
pub(crate) const MIGRATION_V9: &str = r#"
ALTER TABLE symbols ADD COLUMN visibility TEXT;
"#;
pub(crate) const MIGRATION_V10: &str = r#"
ALTER TABLE events ADD COLUMN source TEXT;
ALTER TABLE events ADD COLUMN results INTEGER;
ALTER TABLE events ADD COLUMN flags TEXT;
CREATE TABLE IF NOT EXISTS usage_daily (
day TEXT NOT NULL,
source TEXT NOT NULL,
flags TEXT NOT NULL,
searches INTEGER NOT NULL,
misses INTEGER NOT NULL,
PRIMARY KEY (day, source, flags)
);
"#;
pub(crate) const MIGRATION_V11: &str = r#"
ALTER TABLE events ADD COLUMN status TEXT;
ALTER TABLE events ADD COLUMN coverage TEXT;
ALTER TABLE usage_daily ADD COLUMN warming INTEGER NOT NULL DEFAULT 0;
ALTER TABLE usage_daily ADD COLUMN on_complete INTEGER NOT NULL DEFAULT 0;
"#;
pub(crate) const MIGRATIONS: [(i64, &str); 10] = [
(2, MIGRATION_V2),
(3, MIGRATION_V3),
(4, MIGRATION_V4),
(5, MIGRATION_V5),
(6, MIGRATION_V6),
(7, MIGRATION_V7),
(8, MIGRATION_V8),
(9, MIGRATION_V9),
(10, MIGRATION_V10),
(11, MIGRATION_V11),
];
pub(crate) const FTS_INSERT_TRIGGER: &str = r#"
CREATE TRIGGER IF NOT EXISTS symbols_ai AFTER INSERT ON symbols BEGIN
INSERT INTO symbols_fts(rowid, name) VALUES (new.id, new.name);
END;
"#;