























@@ -21,6 +21,8 @@ const LEGACY_MEMORY_INDEX_TRIGGERS = [
2121"memory_chunks_revision_after_delete",
2222] as const;
232324+const MEMORY_INDEX_SOURCE_COLUMNS = ["path", "source", "hash", "mtime", "size"] as const;
25+2426function tableHasExactColumns(
2527db: DatabaseSync,
2628tableName: string,
@@ -31,13 +33,80 @@ function tableHasExactColumns(
3133return columns.size === expected.length && expected.every((column) => columns.has(column));
3234}
333536+function tablePrimaryKeyColumns(db: DatabaseSync, tableName: string): string[] {
37+const rows = db.prepare(`PRAGMA table_info(${tableName})`).all() as Array<{
38+name?: unknown;
39+pk?: unknown;
40+}>;
41+return rows
42+.flatMap((row) =>
43+typeof row.name === "string" && typeof row.pk === "number" && row.pk > 0
44+ ? [{ name: row.name, pk: row.pk }]
45+ : [],
46+)
47+.toSorted((left, right) => left.pk - right.pk)
48+.map((row) => row.name);
49+}
50+51+function tableHasPrimaryKey(
52+db: DatabaseSync,
53+tableName: string,
54+expectedColumns: readonly string[],
55+): boolean {
56+const columns = tablePrimaryKeyColumns(db, tableName);
57+return (
58+columns.length === expectedColumns.length &&
59+columns.every((column, index) => column === expectedColumns[index])
60+);
61+}
62+3463function assertLegacyRowsCopied(db: DatabaseSync, query: string, tableName: string): void {
3564const row = db.prepare(query).get() as { missing?: unknown } | undefined;
3665if (Number(row?.missing ?? 0) > 0) {
3766throw new Error(`legacy memory ${tableName} rows conflict with canonical memory index rows`);
3867}
3968}
406970+function migrateCanonicalMemoryIndexSourcesPrimaryKey(db: DatabaseSync): void {
71+if (
72+!tableHasExactColumns(db, MEMORY_INDEX_SOURCES_TABLE, MEMORY_INDEX_SOURCE_COLUMNS) ||
73+tableHasPrimaryKey(db, MEMORY_INDEX_SOURCES_TABLE, ["path", "source"])
74+) {
75+return;
76+}
77+if (!tableHasPrimaryKey(db, MEMORY_INDEX_SOURCES_TABLE, ["path"])) {
78+return;
79+}
80+81+db.exec("SAVEPOINT migrate_memory_index_sources_primary_key");
82+try {
83+db.exec(`
84+ DROP TRIGGER IF EXISTS memory_index_sources_revision_after_insert;
85+ DROP TRIGGER IF EXISTS memory_index_sources_revision_after_update;
86+ DROP TRIGGER IF EXISTS memory_index_sources_revision_after_delete;
87+88+ ALTER TABLE ${MEMORY_INDEX_SOURCES_TABLE}
89+ RENAME TO memory_index_sources_path_pk_migration;
90+ CREATE TABLE ${MEMORY_INDEX_SOURCES_TABLE} (
91+ path TEXT NOT NULL,
92+ source TEXT NOT NULL DEFAULT 'memory',
93+ hash TEXT NOT NULL,
94+ mtime INTEGER NOT NULL,
95+ size INTEGER NOT NULL,
96+ PRIMARY KEY (path, source)
97+ );
98+ INSERT INTO ${MEMORY_INDEX_SOURCES_TABLE} (path, source, hash, mtime, size)
99+ SELECT path, source, hash, mtime, size FROM memory_index_sources_path_pk_migration;
100+ DROP TABLE memory_index_sources_path_pk_migration;
101+ RELEASE migrate_memory_index_sources_primary_key;
102+ `);
103+} catch (err) {
104+db.exec("ROLLBACK TO migrate_memory_index_sources_primary_key");
105+db.exec("RELEASE migrate_memory_index_sources_primary_key");
106+throw err;
107+}
108+}
109+41110function migrateLegacyMemoryIndexTables(db: DatabaseSync): void {
42111const hasLegacyCoreTables =
43112tableHasExactColumns(db, "meta", ["key", "value"]) &&
@@ -218,6 +287,9 @@ export function ensureMemoryIndexSchema(params: {
218287 revision INTEGER NOT NULL
219288 );
220289 INSERT OR IGNORE INTO ${MEMORY_INDEX_STATE_TABLE} (id, revision) VALUES (1, 0);
290+ `);
291+migrateCanonicalMemoryIndexSourcesPrimaryKey(params.db);
292+params.db.exec(`
221293222294 CREATE TRIGGER IF NOT EXISTS memory_index_sources_revision_after_insert
223295 AFTER INSERT ON ${MEMORY_INDEX_SOURCES_TABLE}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。