| 227 | /* ── Schema ─────────────────────────────────────────────────────── */ |
| 228 | |
| 229 | static int init_schema(cbm_store_t *s) { |
| 230 | const char *ddl = |
| 231 | "CREATE TABLE IF NOT EXISTS projects (" |
| 232 | " name TEXT PRIMARY KEY," |
| 233 | " indexed_at TEXT NOT NULL," |
| 234 | " root_path TEXT NOT NULL" |
| 235 | ");" |
| 236 | "CREATE TABLE IF NOT EXISTS file_hashes (" |
| 237 | " project TEXT NOT NULL REFERENCES projects(name) ON DELETE CASCADE," |
| 238 | " rel_path TEXT NOT NULL," |
| 239 | " sha256 TEXT NOT NULL," |
| 240 | " mtime_ns INTEGER NOT NULL DEFAULT 0," |
| 241 | " size INTEGER NOT NULL DEFAULT 0," |
| 242 | " PRIMARY KEY (project, rel_path)" |
| 243 | ");" |
| 244 | "CREATE TABLE IF NOT EXISTS nodes (" |
| 245 | " id INTEGER PRIMARY KEY AUTOINCREMENT," |
| 246 | " project TEXT NOT NULL REFERENCES projects(name) ON DELETE CASCADE," |
| 247 | " label TEXT NOT NULL," |
| 248 | " name TEXT NOT NULL," |
| 249 | " qualified_name TEXT NOT NULL," |
| 250 | " file_path TEXT DEFAULT ''," |
| 251 | " start_line INTEGER DEFAULT 0," |
| 252 | " end_line INTEGER DEFAULT 0," |
| 253 | " properties TEXT DEFAULT '{}'," |
| 254 | " UNIQUE(project, qualified_name)" |
| 255 | ");" |
| 256 | /* local_name_gen (#768): IMPORTS edges carry one imported symbol's |
| 257 | * local_name each, so uniqueness must discriminate on it — two named |
| 258 | * imports from the same specifier are distinct edges. Non-IMPORTS |
| 259 | * edges get '' (NOT NULL: NULLs never conflict in a UNIQUE index, |
| 260 | * which would break their dedup entirely). Mirrors the graph-buffer |
| 261 | * dedup key (make_edge_key) and the raw dump writer's DDL + hand- |
| 262 | * built sqlite_autoindex_edges_1 (internal/cbm/sqlite_writer.c) — |
| 263 | * keep all three in sync. */ |
| 264 | "CREATE TABLE IF NOT EXISTS edges (" |
| 265 | " id INTEGER PRIMARY KEY AUTOINCREMENT," |
| 266 | " project TEXT NOT NULL REFERENCES projects(name) ON DELETE CASCADE," |
| 267 | " source_id INTEGER NOT NULL REFERENCES nodes(id) ON DELETE CASCADE," |
| 268 | " target_id INTEGER NOT NULL REFERENCES nodes(id) ON DELETE CASCADE," |
| 269 | " type TEXT NOT NULL," |
| 270 | " properties TEXT DEFAULT '{}'," |
| 271 | " url_path_gen TEXT GENERATED ALWAYS AS (json_extract(properties,'$.url_path'))," |
| 272 | " local_name_gen TEXT GENERATED ALWAYS AS (CASE WHEN type='IMPORTS'" |
| 273 | " THEN coalesce(json_extract(properties,'$.local_name'),'') ELSE '' END)," |
| 274 | " UNIQUE(source_id, target_id, type, local_name_gen)" |
| 275 | ");" |
| 276 | "CREATE TABLE IF NOT EXISTS project_summaries (" |
| 277 | " project TEXT PRIMARY KEY," |
| 278 | " summary TEXT NOT NULL," |
| 279 | " source_hash TEXT NOT NULL," |
| 280 | " created_at TEXT NOT NULL," |
| 281 | " updated_at TEXT NOT NULL" |
| 282 | ");" |
| 283 | /* One row per file: its serialized cross-file LSP surface + the |
| 284 | * closure-repair metadata (surface hash, referenced-name bloom, |
| 285 | * governing-config context). Written at publication from exactly the |
| 286 | * def set pass_lsp_cross registered, so an incremental run can |
no test coverage detected