drizzle-orm 1.0.0-beta.22-41a7d21 → 1.0.0-beta.22-bf0fa46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun-sql/sqlite/migrator.cjs +1 -1
- package/bun-sql/sqlite/migrator.cjs.map +1 -1
- package/bun-sql/sqlite/migrator.js +1 -1
- package/bun-sql/sqlite/migrator.js.map +1 -1
- package/d1/migrator.cjs +1 -1
- package/d1/migrator.cjs.map +1 -1
- package/d1/migrator.js +1 -1
- package/d1/migrator.js.map +1 -1
- package/durable-sqlite/migrator.cjs +1 -1
- package/durable-sqlite/migrator.js +1 -1
- package/libsql/migrator.cjs +1 -1
- package/libsql/migrator.cjs.map +1 -1
- package/libsql/migrator.js +1 -1
- package/libsql/migrator.js.map +1 -1
- package/mysql-proxy/migrator.cjs +2 -2
- package/mysql-proxy/migrator.cjs.map +1 -1
- package/mysql-proxy/migrator.js +2 -2
- package/mysql-proxy/migrator.js.map +1 -1
- package/op-sqlite/migrator.cjs +1 -1
- package/op-sqlite/migrator.cjs.map +1 -1
- package/op-sqlite/migrator.js +1 -1
- package/op-sqlite/migrator.js.map +1 -1
- package/package.json +67 -13
- package/pg-proxy/migrator.cjs +2 -2
- package/pg-proxy/migrator.cjs.map +1 -1
- package/pg-proxy/migrator.js +2 -2
- package/pg-proxy/migrator.js.map +1 -1
- package/singlestore-proxy/migrator.cjs +2 -2
- package/singlestore-proxy/migrator.cjs.map +1 -1
- package/singlestore-proxy/migrator.js +2 -2
- package/singlestore-proxy/migrator.js.map +1 -1
- package/sqlite-cloud/migrator.cjs +1 -1
- package/sqlite-cloud/migrator.cjs.map +1 -1
- package/sqlite-cloud/migrator.js +1 -1
- package/sqlite-cloud/migrator.js.map +1 -1
- package/sqlite-core/dialect.cjs +7 -7
- package/sqlite-core/dialect.cjs.map +1 -1
- package/sqlite-core/dialect.d.cts +2 -1
- package/sqlite-core/dialect.d.ts +2 -1
- package/sqlite-core/dialect.js +7 -7
- package/sqlite-core/dialect.js.map +1 -1
- package/sqlite-proxy/migrator.cjs +40 -2
- package/sqlite-proxy/migrator.cjs.map +1 -1
- package/sqlite-proxy/migrator.js +39 -2
- package/sqlite-proxy/migrator.js.map +1 -1
- package/tursodatabase/migrator.cjs +1 -1
- package/tursodatabase/migrator.cjs.map +1 -1
- package/tursodatabase/migrator.js +1 -1
- package/tursodatabase/migrator.js.map +1 -1
- package/up-migrations/cockroach.cjs +6 -12
- package/up-migrations/cockroach.cjs.map +1 -1
- package/up-migrations/cockroach.d.cts +1 -5
- package/up-migrations/cockroach.d.ts +1 -5
- package/up-migrations/cockroach.js +6 -12
- package/up-migrations/cockroach.js.map +1 -1
- package/up-migrations/effect-pg.cjs +6 -12
- package/up-migrations/effect-pg.cjs.map +1 -1
- package/up-migrations/effect-pg.d.cts +1 -5
- package/up-migrations/effect-pg.d.ts +1 -5
- package/up-migrations/effect-pg.js +6 -12
- package/up-migrations/effect-pg.js.map +1 -1
- package/up-migrations/mssql.cjs +6 -12
- package/up-migrations/mssql.cjs.map +1 -1
- package/up-migrations/mssql.d.cts +1 -5
- package/up-migrations/mssql.d.ts +1 -5
- package/up-migrations/mssql.js +6 -12
- package/up-migrations/mssql.js.map +1 -1
- package/up-migrations/mysql-proxy.cjs +73 -0
- package/up-migrations/mysql-proxy.cjs.map +1 -0
- package/up-migrations/mysql-proxy.d.cts +16 -0
- package/up-migrations/mysql-proxy.d.ts +16 -0
- package/up-migrations/mysql-proxy.js +71 -0
- package/up-migrations/mysql-proxy.js.map +1 -0
- package/up-migrations/mysql.cjs +12 -28
- package/up-migrations/mysql.cjs.map +1 -1
- package/up-migrations/mysql.d.cts +1 -5
- package/up-migrations/mysql.d.ts +1 -5
- package/up-migrations/mysql.js +12 -28
- package/up-migrations/mysql.js.map +1 -1
- package/up-migrations/pg-proxy.cjs +79 -0
- package/up-migrations/pg-proxy.cjs.map +1 -0
- package/up-migrations/pg-proxy.d.cts +16 -0
- package/up-migrations/pg-proxy.d.ts +16 -0
- package/up-migrations/pg-proxy.js +77 -0
- package/up-migrations/pg-proxy.js.map +1 -0
- package/up-migrations/pg.cjs +9 -13
- package/up-migrations/pg.cjs.map +1 -1
- package/up-migrations/pg.d.cts +2 -6
- package/up-migrations/pg.d.ts +2 -6
- package/up-migrations/pg.js +9 -13
- package/up-migrations/pg.js.map +1 -1
- package/up-migrations/singlestore-proxy.cjs +73 -0
- package/up-migrations/singlestore-proxy.cjs.map +1 -0
- package/up-migrations/singlestore-proxy.d.cts +16 -0
- package/up-migrations/singlestore-proxy.d.ts +16 -0
- package/up-migrations/singlestore-proxy.js +71 -0
- package/up-migrations/singlestore-proxy.js.map +1 -0
- package/up-migrations/singlestore.cjs +9 -25
- package/up-migrations/singlestore.cjs.map +1 -1
- package/up-migrations/singlestore.d.cts +1 -5
- package/up-migrations/singlestore.d.ts +1 -5
- package/up-migrations/singlestore.js +9 -25
- package/up-migrations/singlestore.js.map +1 -1
- package/up-migrations/sqlite-proxy.cjs +80 -0
- package/up-migrations/sqlite-proxy.cjs.map +1 -0
- package/up-migrations/sqlite-proxy.d.cts +16 -0
- package/up-migrations/sqlite-proxy.d.ts +16 -0
- package/up-migrations/sqlite-proxy.js +78 -0
- package/up-migrations/sqlite-proxy.js.map +1 -0
- package/up-migrations/sqlite.cjs +17 -48
- package/up-migrations/sqlite.cjs.map +1 -1
- package/up-migrations/sqlite.d.cts +3 -10
- package/up-migrations/sqlite.d.ts +5 -12
- package/up-migrations/sqlite.js +17 -48
- package/up-migrations/sqlite.js.map +1 -1
- package/up-migrations/utils.cjs +47 -0
- package/up-migrations/utils.cjs.map +1 -0
- package/up-migrations/utils.d.cts +25 -0
- package/up-migrations/utils.d.ts +25 -0
- package/up-migrations/utils.js +44 -0
- package/up-migrations/utils.js.map +1 -0
- package/sqlite-proxy/migrator.internal.cjs +0 -61
- package/sqlite-proxy/migrator.internal.cjs.map +0 -1
- package/sqlite-proxy/migrator.internal.d.cts +0 -26
- package/sqlite-proxy/migrator.internal.d.ts +0 -26
- package/sqlite-proxy/migrator.internal.js +0 -59
- package/sqlite-proxy/migrator.internal.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"singlestore.cjs","names":["sql"],"sources":["../../src/up-migrations/singlestore.ts"],"sourcesContent":["import type { MigrationMeta } from '~/migrator.ts';\nimport type { SingleStoreSession } from '~/singlestore-core/session.ts';\nimport {
|
|
1
|
+
{"version":3,"file":"singlestore.cjs","names":["sql","GET_VERSION_FOR","MIGRATIONS_TABLE_VERSIONS"],"sources":["../../src/up-migrations/singlestore.ts"],"sourcesContent":["import type { MigrationMeta } from '~/migrator.ts';\nimport type { SingleStoreSession } from '~/singlestore-core/session.ts';\nimport { sql } from '~/sql/sql.ts';\nimport { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS, type UpgradeResult } from './utils.ts';\n\n/**\n * Map of upgrade functions. Each key is the version being upgraded FROM,\n * and the function upgrades the table to the next version.\n */\nconst upgradeFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SingleStoreSession,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\tconst dbRows = await session.all<{ id: number; hash: string; created_at: string }>(\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\t\tconst toApply: { id: number; name: string }[] = [];\n\t\tlet unmatchedIds: number[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash === dbRow.hash);\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t}\n\n\t\t\tif (matched) toApply.push({ id: dbRow.id, name: matched.name });\n\t\t\telse unmatchedIds.push(dbRow.id);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatchedIds.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatchedIds.length} migrations (ids: ${\n\t\t\t\t\tunmatchedIds.join(', ')\n\t\t\t\t}) in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tawait session.execute(sql`ALTER TABLE ${table} ADD ${sql.identifier('name')} text`);\n\t\tawait session.execute(\n\t\t\tsql`ALTER TABLE ${table} ADD ${sql.identifier('applied_at')} TIMESTAMP DEFAULT CURRENT_TIMESTAMP`,\n\t\t);\n\n\t\tfor (const backfillEntry of toApply) {\n\t\t\tawait session.execute(\n\t\t\t\tsql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE ${sql.identifier('id')} = ${backfillEntry.id}`,\n\t\t\t);\n\t\t}\n\t},\n};\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeIfNeeded(\n\tmigrationsTable: string,\n\tsession: SingleStoreSession,\n\tlocalMigrations: MigrationMeta[],\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst result = await session.all<{ '1': 1 }>(\n\t\tsql`SELECT 1 FROM information_schema.tables \n\t\t\tWHERE table_name = ${migrationsTable}\n\t\t\tAND table_schema = DATABASE()`,\n\t);\n\n\tif (result.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = await session.all<{ column_name: string }>(\n\t\tsql`SELECT column_name as \\`column_name\\`\n\t\tFROM information_schema.columns\n\t\tWHERE table_name = ${migrationsTable}\n\t\tAND table_schema = DATABASE()\n\t\tORDER BY ordinal_position`,\n\t);\n\n\tconst version = GET_VERSION_FOR.singlestore(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < MIGRATIONS_TABLE_VERSIONS.singlestore; v++) {\n\t\tconst upgradeFn = upgradeFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { newDb: false };\n}\n"],"mappings":";;;;;;;;;;AASA,MAAM,mBAOF,EAUH,GAAG,OAAO,iBAAiB,SAAS,oBAAoB;CACvD,MAAM,QAAQ,gBAAG,GAAGA,iBAAI,WAAW,gBAAgB;CAInD,MAAM,SAAS,MAAM,QAAQ,IAC5B,gBAAG,oCAAoC,MAAM,kBAC7C;AAGD,iBAAgB,MAAM,GAAG,MACxB,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,QAAQ,IAAI,cAAc,EAAE,QAAQ,GAAG,CAChH;CAED,MAAM,2BAAW,IAAI,KAA8B;CACnD,MAAM,yBAAS,IAAI,KAA4B;AAC/C,MAAK,MAAM,MAAM,iBAAiB;AACjC,MAAI,CAAC,SAAS,IAAI,GAAG,aAAa,CACjC,UAAS,IAAI,GAAG,cAAc,EAAE,CAAC;AAElC,WAAS,IAAI,GAAG,aAAa,CAAE,KAAK,GAAG;AACvC,SAAO,IAAI,GAAG,MAAM,GAAG;;CAKxB,MAAM,UAA0C,EAAE;CAClD,IAAI,eAAyB,EAAE;AAE/B,MAAK,MAAM,SAAS,QAAQ;EAC3B,MAAM,cAAc,OAAO,MAAM,WAAW;EAC5C,MAAM,SAAS,OAAO,YAAY,UAAU,GAAG,YAAY,SAAS,EAAE,GAAG,MAAM;EAC/E,MAAM,aAAa,SAAS,IAAI,OAAO;EAEvC,IAAI;AAEJ,MAAI,cAAc,WAAW,WAAW,EACvC,WAAU,WAAW;WACX,cAAc,WAAW,SAAS,EAC5C,WAAU,WAAW,MAAM,MAAM,EAAE,SAAS,MAAM,KAAK;MAEvD,WAAU,OAAO,IAAI,MAAM,KAAK;AAGjC,MAAI,QAAS,SAAQ,KAAK;GAAE,IAAI,MAAM;GAAI,MAAM,QAAQ;GAAM,CAAC;MAC1D,cAAa,KAAK,MAAM,GAAG;;AAQjC,KAAI,aAAa,SAAS,EACzB,OAAM,MACL,2DAA2D,aAAa,OAAO,oBAC9E,aAAa,KAAK,KAAK,CACvB,kKACD;AAIF,OAAM,QAAQ,QAAQ,gBAAG,eAAe,MAAM,OAAOA,iBAAI,WAAW,OAAO,CAAC,OAAO;AACnF,OAAM,QAAQ,QACb,gBAAG,eAAe,MAAM,OAAOA,iBAAI,WAAW,aAAa,CAAC,sCAC5D;AAED,MAAK,MAAM,iBAAiB,QAC3B,OAAM,QAAQ,QACb,gBAAG,UAAU,MAAM,OAAOA,iBAAI,WAAW,OAAO,CAAC,KAAK,cAAc,KAAK,IACxEA,iBAAI,WAAW,aAAa,CAC5B,gBAAgBA,iBAAI,WAAW,KAAK,CAAC,KAAK,cAAc,KACzD;GAGH;;;;;;;AAQD,eAAsB,gBACrB,iBACA,SACA,iBACyB;AAQzB,MANe,MAAM,QAAQ,IAC5B,gBAAG;wBACmB,gBAAgB;kCAEtC,EAEU,WAAW,EACrB,QAAO,EAAE,OAAO,MAAM;CAIvB,MAAM,OAAO,MAAM,QAAQ,IAC1B,gBAAG;;uBAEkB,gBAAgB;;6BAGrC;CAED,MAAM,UAAUC,4CAAgB,YAAY,KAAK,KAAK,MAAM,EAAE,YAAY,CAAC;AAE3E,MAAK,IAAI,IAAI,SAAS,IAAIC,sDAA0B,aAAa,KAAK;EACrE,MAAM,YAAY,iBAAiB;AACnC,MAAI,CAAC,UACJ,OAAM,IAAI,MAAM,gDAAgD,EAAE,MAAM,IAAI,IAAI;AAEjF,QAAM,UAAU,iBAAiB,SAAS,gBAAgB;;AAG3D,QAAO,EAAE,OAAO,OAAO"}
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
+
import { UpgradeResult } from "./utils.cjs";
|
|
1
2
|
import { MigrationMeta } from "../migrator.cjs";
|
|
2
3
|
import { SingleStoreSession } from "../singlestore-core/session.cjs";
|
|
3
4
|
|
|
4
5
|
//#region src/up-migrations/singlestore.d.ts
|
|
5
|
-
interface UpgradeResult {
|
|
6
|
-
newDb?: boolean;
|
|
7
|
-
prevVersion?: number;
|
|
8
|
-
currentVersion?: number;
|
|
9
|
-
}
|
|
10
6
|
/**
|
|
11
7
|
* Detects the current version of the migrations table schema and upgrades it if needed.
|
|
12
8
|
*
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
+
import { UpgradeResult } from "./utils.js";
|
|
1
2
|
import { MigrationMeta } from "../migrator.js";
|
|
2
3
|
import { SingleStoreSession } from "../singlestore-core/session.js";
|
|
3
4
|
|
|
4
5
|
//#region src/up-migrations/singlestore.d.ts
|
|
5
|
-
interface UpgradeResult {
|
|
6
|
-
newDb?: boolean;
|
|
7
|
-
prevVersion?: number;
|
|
8
|
-
currentVersion?: number;
|
|
9
|
-
}
|
|
10
6
|
/**
|
|
11
7
|
* Detects the current version of the migrations table schema and upgrades it if needed.
|
|
12
8
|
*
|
|
@@ -1,28 +1,14 @@
|
|
|
1
|
+
import { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS } from "./utils.js";
|
|
1
2
|
import { sql } from "../sql/sql.js";
|
|
2
3
|
|
|
3
4
|
//#region src/up-migrations/singlestore.ts
|
|
4
|
-
const CURRENT_MIGRATION_TABLE_VERSION = 1;
|
|
5
|
-
function getVersion(columns) {
|
|
6
|
-
if (columns.includes("name")) return 1;
|
|
7
|
-
return 0;
|
|
8
|
-
}
|
|
9
|
-
async function all(session, sqlQuery, resultMapper = () => []) {
|
|
10
|
-
const result = await session.all(sqlQuery);
|
|
11
|
-
if (result.length === 0) return [];
|
|
12
|
-
if (Array.isArray(result[0])) return result.map((row) => resultMapper(row));
|
|
13
|
-
return result;
|
|
14
|
-
}
|
|
15
5
|
/**
|
|
16
6
|
* Map of upgrade functions. Each key is the version being upgraded FROM,
|
|
17
7
|
* and the function upgrades the table to the next version.
|
|
18
8
|
*/
|
|
19
9
|
const upgradeFunctions = { 0: async (migrationsTable, session, localMigrations) => {
|
|
20
10
|
const table = sql`${sql.identifier(migrationsTable)}`;
|
|
21
|
-
const dbRows = await all(
|
|
22
|
-
id: row[0],
|
|
23
|
-
hash: row[1],
|
|
24
|
-
created_at: row[2]
|
|
25
|
-
}));
|
|
11
|
+
const dbRows = await session.all(sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`);
|
|
26
12
|
localMigrations.sort((a, b) => a.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? "").localeCompare(b.name ?? ""));
|
|
27
13
|
const byMillis = /* @__PURE__ */ new Map();
|
|
28
14
|
const byHash = /* @__PURE__ */ new Map();
|
|
@@ -59,23 +45,21 @@ const upgradeFunctions = { 0: async (migrationsTable, session, localMigrations)
|
|
|
59
45
|
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
60
46
|
*/
|
|
61
47
|
async function upgradeIfNeeded(migrationsTable, session, localMigrations) {
|
|
62
|
-
if ((await all(
|
|
48
|
+
if ((await session.all(sql`SELECT 1 FROM information_schema.tables
|
|
63
49
|
WHERE table_name = ${migrationsTable}
|
|
64
|
-
AND table_schema = DATABASE()
|
|
65
|
-
const
|
|
50
|
+
AND table_schema = DATABASE()`)).length === 0) return { newDb: true };
|
|
51
|
+
const rows = await session.all(sql`SELECT column_name as \`column_name\`
|
|
66
52
|
FROM information_schema.columns
|
|
67
53
|
WHERE table_name = ${migrationsTable}
|
|
68
54
|
AND table_schema = DATABASE()
|
|
69
|
-
ORDER BY ordinal_position
|
|
70
|
-
|
|
55
|
+
ORDER BY ordinal_position`);
|
|
56
|
+
const version = GET_VERSION_FOR.singlestore(rows.map((r) => r.column_name));
|
|
57
|
+
for (let v = version; v < MIGRATIONS_TABLE_VERSIONS.singlestore; v++) {
|
|
71
58
|
const upgradeFn = upgradeFunctions[v];
|
|
72
59
|
if (!upgradeFn) throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);
|
|
73
60
|
await upgradeFn(migrationsTable, session, localMigrations);
|
|
74
61
|
}
|
|
75
|
-
return {
|
|
76
|
-
prevVersion: version,
|
|
77
|
-
currentVersion: CURRENT_MIGRATION_TABLE_VERSION
|
|
78
|
-
};
|
|
62
|
+
return { newDb: false };
|
|
79
63
|
}
|
|
80
64
|
|
|
81
65
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"singlestore.js","names":[],"sources":["../../src/up-migrations/singlestore.ts"],"sourcesContent":["import type { MigrationMeta } from '~/migrator.ts';\nimport type { SingleStoreSession } from '~/singlestore-core/session.ts';\nimport {
|
|
1
|
+
{"version":3,"file":"singlestore.js","names":[],"sources":["../../src/up-migrations/singlestore.ts"],"sourcesContent":["import type { MigrationMeta } from '~/migrator.ts';\nimport type { SingleStoreSession } from '~/singlestore-core/session.ts';\nimport { sql } from '~/sql/sql.ts';\nimport { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS, type UpgradeResult } from './utils.ts';\n\n/**\n * Map of upgrade functions. Each key is the version being upgraded FROM,\n * and the function upgrades the table to the next version.\n */\nconst upgradeFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tsession: SingleStoreSession,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, session, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\tconst dbRows = await session.all<{ id: number; hash: string; created_at: string }>(\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t);\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\t\tconst toApply: { id: number; name: string }[] = [];\n\t\tlet unmatchedIds: number[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash === dbRow.hash);\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t}\n\n\t\t\tif (matched) toApply.push({ id: dbRow.id, name: matched.name });\n\t\t\telse unmatchedIds.push(dbRow.id);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatchedIds.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatchedIds.length} migrations (ids: ${\n\t\t\t\t\tunmatchedIds.join(', ')\n\t\t\t\t}) in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tawait session.execute(sql`ALTER TABLE ${table} ADD ${sql.identifier('name')} text`);\n\t\tawait session.execute(\n\t\t\tsql`ALTER TABLE ${table} ADD ${sql.identifier('applied_at')} TIMESTAMP DEFAULT CURRENT_TIMESTAMP`,\n\t\t);\n\n\t\tfor (const backfillEntry of toApply) {\n\t\t\tawait session.execute(\n\t\t\t\tsql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\t\tsql.identifier('applied_at')\n\t\t\t\t} = NULL WHERE ${sql.identifier('id')} = ${backfillEntry.id}`,\n\t\t\t);\n\t\t}\n\t},\n};\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeIfNeeded(\n\tmigrationsTable: string,\n\tsession: SingleStoreSession,\n\tlocalMigrations: MigrationMeta[],\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst result = await session.all<{ '1': 1 }>(\n\t\tsql`SELECT 1 FROM information_schema.tables \n\t\t\tWHERE table_name = ${migrationsTable}\n\t\t\tAND table_schema = DATABASE()`,\n\t);\n\n\tif (result.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = await session.all<{ column_name: string }>(\n\t\tsql`SELECT column_name as \\`column_name\\`\n\t\tFROM information_schema.columns\n\t\tWHERE table_name = ${migrationsTable}\n\t\tAND table_schema = DATABASE()\n\t\tORDER BY ordinal_position`,\n\t);\n\n\tconst version = GET_VERSION_FOR.singlestore(rows.map((r) => r.column_name));\n\n\tfor (let v = version; v < MIGRATIONS_TABLE_VERSIONS.singlestore; v++) {\n\t\tconst upgradeFn = upgradeFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, session, localMigrations);\n\t}\n\n\treturn { newDb: false };\n}\n"],"mappings":";;;;;;;;AASA,MAAM,mBAOF,EAUH,GAAG,OAAO,iBAAiB,SAAS,oBAAoB;CACvD,MAAM,QAAQ,GAAG,GAAG,IAAI,WAAW,gBAAgB;CAInD,MAAM,SAAS,MAAM,QAAQ,IAC5B,GAAG,oCAAoC,MAAM,kBAC7C;AAGD,iBAAgB,MAAM,GAAG,MACxB,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,QAAQ,IAAI,cAAc,EAAE,QAAQ,GAAG,CAChH;CAED,MAAM,2BAAW,IAAI,KAA8B;CACnD,MAAM,yBAAS,IAAI,KAA4B;AAC/C,MAAK,MAAM,MAAM,iBAAiB;AACjC,MAAI,CAAC,SAAS,IAAI,GAAG,aAAa,CACjC,UAAS,IAAI,GAAG,cAAc,EAAE,CAAC;AAElC,WAAS,IAAI,GAAG,aAAa,CAAE,KAAK,GAAG;AACvC,SAAO,IAAI,GAAG,MAAM,GAAG;;CAKxB,MAAM,UAA0C,EAAE;CAClD,IAAI,eAAyB,EAAE;AAE/B,MAAK,MAAM,SAAS,QAAQ;EAC3B,MAAM,cAAc,OAAO,MAAM,WAAW;EAC5C,MAAM,SAAS,OAAO,YAAY,UAAU,GAAG,YAAY,SAAS,EAAE,GAAG,MAAM;EAC/E,MAAM,aAAa,SAAS,IAAI,OAAO;EAEvC,IAAI;AAEJ,MAAI,cAAc,WAAW,WAAW,EACvC,WAAU,WAAW;WACX,cAAc,WAAW,SAAS,EAC5C,WAAU,WAAW,MAAM,MAAM,EAAE,SAAS,MAAM,KAAK;MAEvD,WAAU,OAAO,IAAI,MAAM,KAAK;AAGjC,MAAI,QAAS,SAAQ,KAAK;GAAE,IAAI,MAAM;GAAI,MAAM,QAAQ;GAAM,CAAC;MAC1D,cAAa,KAAK,MAAM,GAAG;;AAQjC,KAAI,aAAa,SAAS,EACzB,OAAM,MACL,2DAA2D,aAAa,OAAO,oBAC9E,aAAa,KAAK,KAAK,CACvB,kKACD;AAIF,OAAM,QAAQ,QAAQ,GAAG,eAAe,MAAM,OAAO,IAAI,WAAW,OAAO,CAAC,OAAO;AACnF,OAAM,QAAQ,QACb,GAAG,eAAe,MAAM,OAAO,IAAI,WAAW,aAAa,CAAC,sCAC5D;AAED,MAAK,MAAM,iBAAiB,QAC3B,OAAM,QAAQ,QACb,GAAG,UAAU,MAAM,OAAO,IAAI,WAAW,OAAO,CAAC,KAAK,cAAc,KAAK,IACxE,IAAI,WAAW,aAAa,CAC5B,gBAAgB,IAAI,WAAW,KAAK,CAAC,KAAK,cAAc,KACzD;GAGH;;;;;;;AAQD,eAAsB,gBACrB,iBACA,SACA,iBACyB;AAQzB,MANe,MAAM,QAAQ,IAC5B,GAAG;wBACmB,gBAAgB;kCAEtC,EAEU,WAAW,EACrB,QAAO,EAAE,OAAO,MAAM;CAIvB,MAAM,OAAO,MAAM,QAAQ,IAC1B,GAAG;;uBAEkB,gBAAgB;;6BAGrC;CAED,MAAM,UAAU,gBAAgB,YAAY,KAAK,KAAK,MAAM,EAAE,YAAY,CAAC;AAE3E,MAAK,IAAI,IAAI,SAAS,IAAI,0BAA0B,aAAa,KAAK;EACrE,MAAM,YAAY,iBAAiB;AACnC,MAAI,CAAC,UACJ,OAAM,IAAI,MAAM,gDAAgD,EAAE,MAAM,IAAI,IAAI;AAEjF,QAAM,UAAU,iBAAiB,SAAS,gBAAgB;;AAG3D,QAAO,EAAE,OAAO,OAAO"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
const require_up_migrations_utils = require('./utils.cjs');
|
|
4
|
+
let __sql_sql_ts = require("../sql/sql.cjs");
|
|
5
|
+
|
|
6
|
+
//#region src/up-migrations/sqlite-proxy.ts
|
|
7
|
+
/**
|
|
8
|
+
* Detects the current version of the migrations table schema and upgrades it if needed.
|
|
9
|
+
*
|
|
10
|
+
* Version 0: Original schema (id, hash, created_at)
|
|
11
|
+
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
12
|
+
*/
|
|
13
|
+
async function upgradeAsyncIfNeeded(migrationsTable, db, callback, localMigrations) {
|
|
14
|
+
if ((await db.session.values(__sql_sql_ts.sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`)).length === 0) return { newDb: true };
|
|
15
|
+
const rows = await db.session.values(__sql_sql_ts.sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`);
|
|
16
|
+
const version = require_up_migrations_utils.GET_VERSION_FOR.sqlite(rows.map((r) => r[0]));
|
|
17
|
+
for (let v = version; v < require_up_migrations_utils.MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {
|
|
18
|
+
const upgradeFn = upgradeAsyncFunctions[v];
|
|
19
|
+
if (!upgradeFn) throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);
|
|
20
|
+
await upgradeFn(migrationsTable, db, callback, localMigrations);
|
|
21
|
+
}
|
|
22
|
+
return { newDb: false };
|
|
23
|
+
}
|
|
24
|
+
const upgradeAsyncFunctions = { 0: async (migrationsTable, db, callback, localMigrations) => {
|
|
25
|
+
const table = __sql_sql_ts.sql`${__sql_sql_ts.sql.identifier(migrationsTable)}`;
|
|
26
|
+
const dbRows = (await db.session.values(__sql_sql_ts.sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`)).map((row) => ({
|
|
27
|
+
id: row[0],
|
|
28
|
+
hash: row[1],
|
|
29
|
+
created_at: row[2]
|
|
30
|
+
}));
|
|
31
|
+
localMigrations.sort((a, b) => a.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? "").localeCompare(b.name ?? ""));
|
|
32
|
+
const byMillis = /* @__PURE__ */ new Map();
|
|
33
|
+
const byHash = /* @__PURE__ */ new Map();
|
|
34
|
+
for (const lm of localMigrations) {
|
|
35
|
+
if (!byMillis.has(lm.folderMillis)) byMillis.set(lm.folderMillis, []);
|
|
36
|
+
byMillis.get(lm.folderMillis).push(lm);
|
|
37
|
+
byHash.set(lm.hash, lm);
|
|
38
|
+
}
|
|
39
|
+
const toApply = [];
|
|
40
|
+
let unmatched = [];
|
|
41
|
+
for (const dbRow of dbRows) {
|
|
42
|
+
const stringified = String(dbRow.created_at);
|
|
43
|
+
const millis = Number(stringified.substring(0, stringified.length - 3) + "000");
|
|
44
|
+
const candidates = byMillis.get(millis);
|
|
45
|
+
let matched;
|
|
46
|
+
let matchedBy = null;
|
|
47
|
+
if (candidates && candidates.length === 1) {
|
|
48
|
+
matched = candidates[0];
|
|
49
|
+
matchedBy = "millis";
|
|
50
|
+
} else if (candidates && candidates.length > 1) {
|
|
51
|
+
matched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash);
|
|
52
|
+
if (matched) matchedBy = "hash";
|
|
53
|
+
} else {
|
|
54
|
+
matched = byHash.get(dbRow.hash);
|
|
55
|
+
if (matched) matchedBy = "hash";
|
|
56
|
+
}
|
|
57
|
+
if (matched) toApply.push({
|
|
58
|
+
id: dbRow.id,
|
|
59
|
+
name: matched.name,
|
|
60
|
+
hash: dbRow.hash,
|
|
61
|
+
created_at: stringified,
|
|
62
|
+
matchedBy: dbRow.id ? "id" : matchedBy
|
|
63
|
+
});
|
|
64
|
+
else unmatched.push(dbRow);
|
|
65
|
+
}
|
|
66
|
+
if (unmatched.length > 0) throw Error(`While upgrading your database migrations table we found ${unmatched.length} (${unmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(", ")}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`);
|
|
67
|
+
const statements = [db.dialect.sqlToQuery(__sql_sql_ts.sql`ALTER TABLE ${table} ADD COLUMN ${__sql_sql_ts.sql.identifier("name")} text`.inlineParams()).sql, db.dialect.sqlToQuery(__sql_sql_ts.sql`ALTER TABLE ${table} ADD COLUMN ${__sql_sql_ts.sql.identifier("applied_at")} TEXT`.inlineParams()).sql];
|
|
68
|
+
for (const backfillEntry of toApply) {
|
|
69
|
+
const updateQuery = __sql_sql_ts.sql`UPDATE ${table} SET ${__sql_sql_ts.sql.identifier("name")} = ${backfillEntry.name}, ${__sql_sql_ts.sql.identifier("applied_at")} = NULL WHERE`;
|
|
70
|
+
if (backfillEntry.id) updateQuery.append(__sql_sql_ts.sql` ${__sql_sql_ts.sql.identifier("id")} = ${backfillEntry.id}`);
|
|
71
|
+
else if (backfillEntry.matchedBy === "millis") updateQuery.append(__sql_sql_ts.sql` ${__sql_sql_ts.sql.identifier("created_at")} = ${backfillEntry.created_at}`);
|
|
72
|
+
else updateQuery.append(__sql_sql_ts.sql` ${__sql_sql_ts.sql.identifier("hash")} = ${backfillEntry.hash}`);
|
|
73
|
+
statements.push(db.dialect.sqlToQuery(updateQuery.inlineParams()).sql);
|
|
74
|
+
}
|
|
75
|
+
await callback(statements);
|
|
76
|
+
} };
|
|
77
|
+
|
|
78
|
+
//#endregion
|
|
79
|
+
exports.upgradeAsyncIfNeeded = upgradeAsyncIfNeeded;
|
|
80
|
+
//# sourceMappingURL=sqlite-proxy.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-proxy.cjs","names":["GET_VERSION_FOR","MIGRATIONS_TABLE_VERSIONS","sql"],"sources":["../../src/up-migrations/sqlite-proxy.ts"],"sourcesContent":["import type { MigrationMeta } from '~/migrator.ts';\nimport { sql } from '~/sql/sql.ts';\nimport type { BaseSQLiteDatabase } from '~/sqlite-core/index.ts';\nimport type { SqliteRemoteDatabase } from '~/sqlite-proxy/index.ts';\nimport type { ProxyMigrator } from '~/sqlite-proxy/migrator.ts';\nimport { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS, type UpgradeResult } from './utils.ts';\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeAsyncIfNeeded(\n\tmigrationsTable: string,\n\tdb: SqliteRemoteDatabase<Record<string, unknown>>,\n\tcallback: ProxyMigrator,\n\tlocalMigrations: MigrationMeta[],\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst tableExists = await db.session.values<[1]>(\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = await db.session.values<[string]>(\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t);\n\n\tconst version = GET_VERSION_FOR.sqlite(rows.map((r) => r[0]));\n\n\tfor (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {\n\t\tconst upgradeFn = upgradeAsyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, db, callback, localMigrations);\n\t}\n\n\treturn { newDb: false };\n}\n\nconst upgradeAsyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tdb: BaseSQLiteDatabase<'async', unknown, Record<string, unknown>>,\n\t\tcallback: ProxyMigrator,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, db, callback, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = (await db.session.values<[number | null, string, number]>(\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t)).map((row) => ({\n\t\t\tid: row[0],\n\t\t\thash: row[1],\n\t\t\tcreated_at: row[2],\n\t\t}));\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tconst statements: string[] = [\n\t\t\tdb.dialect.sqlToQuery(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`.inlineParams()).sql,\n\t\t\tdb.dialect.sqlToQuery(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`.inlineParams())\n\t\t\t\t.sql,\n\t\t];\n\t\tfor (const backfillEntry of toApply) {\n\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\tsql.identifier('applied_at')\n\t\t\t} = NULL WHERE`;\n\n\t\t\t// id\n\t\t\t// created_at\n\t\t\t// hash\n\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\tstatements.push(db.dialect.sqlToQuery(updateQuery.inlineParams()).sql);\n\t\t}\n\n\t\tawait callback(statements);\n\t\treturn;\n\t},\n};\n"],"mappings":";;;;;;;;;;;;AAaA,eAAsB,qBACrB,iBACA,IACA,UACA,iBACyB;AAMzB,MAJoB,MAAM,GAAG,QAAQ,OACpC,gBAAG,+DAA+D,kBAClE,EAEe,WAAW,EAC1B,QAAO,EAAE,OAAO,MAAM;CAIvB,MAAM,OAAO,MAAM,GAAG,QAAQ,OAC7B,gBAAG,qDAAqD,gBAAgB,GACxE;CAED,MAAM,UAAUA,4CAAgB,OAAO,KAAK,KAAK,MAAM,EAAE,GAAG,CAAC;AAE7D,MAAK,IAAI,IAAI,SAAS,IAAIC,sDAA0B,QAAQ,KAAK;EAChE,MAAM,YAAY,sBAAsB;AACxC,MAAI,CAAC,UACJ,OAAM,IAAI,MAAM,gDAAgD,EAAE,MAAM,IAAI,IAAI;AAEjF,QAAM,UAAU,iBAAiB,IAAI,UAAU,gBAAgB;;AAGhE,QAAO,EAAE,OAAO,OAAO;;AAGxB,MAAM,wBAQF,EAUH,GAAG,OAAO,iBAAiB,IAAI,UAAU,oBAAoB;CAC5D,MAAM,QAAQ,gBAAG,GAAGC,iBAAI,WAAW,gBAAgB;CAKnD,MAAM,UAAU,MAAM,GAAG,QAAQ,OAChC,gBAAG,oCAAoC,MAAM,kBAC7C,EAAE,KAAK,SAAS;EAChB,IAAI,IAAI;EACR,MAAM,IAAI;EACV,YAAY,IAAI;EAChB,EAAE;AAGH,iBAAgB,MAAM,GAAG,MACxB,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,QAAQ,IAAI,cAAc,EAAE,QAAQ,GAAG,CAChH;CAED,MAAM,2BAAW,IAAI,KAA8B;CACnD,MAAM,yBAAS,IAAI,KAA4B;AAC/C,MAAK,MAAM,MAAM,iBAAiB;AACjC,MAAI,CAAC,SAAS,IAAI,GAAG,aAAa,CACjC,UAAS,IAAI,GAAG,cAAc,EAAE,CAAC;AAElC,WAAS,IAAI,GAAG,aAAa,CAAE,KAAK,GAAG;AACvC,SAAO,IAAI,GAAG,MAAM,GAAG;;CAOxB,MAAM,UAMA,EAAE;CAIR,IAAI,YAAuE,EAAE;AAE7E,MAAK,MAAM,SAAS,QAAQ;EAC3B,MAAM,cAAc,OAAO,MAAM,WAAW;EAC5C,MAAM,SAAS,OAAO,YAAY,UAAU,GAAG,YAAY,SAAS,EAAE,GAAG,MAAM;EAC/E,MAAM,aAAa,SAAS,IAAI,OAAO;EAEvC,IAAI;EACJ,IAAI,YAAsC;AAC1C,MAAI,cAAc,WAAW,WAAW,GAAG;AAC1C,aAAU,WAAW;AACrB,eAAY;aACF,cAAc,WAAW,SAAS,GAAG;AAC/C,aAAU,WAAW,MAAM,MAAM,EAAE,QAAQ,MAAM,QAAQ,EAAE,SAAS,MAAM,KAAK;AAC/E,OAAI,QAAS,aAAY;SACnB;AACN,aAAU,OAAO,IAAI,MAAM,KAAK;AAChC,OAAI,QAAS,aAAY;;AAG1B,MAAI,QACH,SAAQ,KAAK;GACZ,IAAI,MAAM;GACV,MAAM,QAAQ;GACd,MAAM,MAAM;GACZ,YAAY;GACZ,WAAW,MAAM,KAAK,OAAO;GAC7B,CAAC;MACI,WAAU,KAAK,MAAM;;AAQ7B,KAAI,UAAU,SAAS,EACtB,OAAM,MACL,2DAA2D,UAAU,OAAO,IAC3E,UAAU,KAAK,OAAO,QAAQ,GAAG,GAAG,gBAAgB,GAAG,WAAW,GAAG,CAAC,KAAK,KAAK,CAChF,6KACD;CAIF,MAAM,aAAuB,CAC5B,GAAG,QAAQ,WAAW,gBAAG,eAAe,MAAM,cAAcA,iBAAI,WAAW,OAAO,CAAC,OAAO,cAAc,CAAC,CAAC,KAC1G,GAAG,QAAQ,WAAW,gBAAG,eAAe,MAAM,cAAcA,iBAAI,WAAW,aAAa,CAAC,OAAO,cAAc,CAAC,CAC7G,IACF;AACD,MAAK,MAAM,iBAAiB,SAAS;EACpC,MAAM,cAAc,gBAAG,UAAU,MAAM,OAAOA,iBAAI,WAAW,OAAO,CAAC,KAAK,cAAc,KAAK,IAC5FA,iBAAI,WAAW,aAAa,CAC5B;AAKD,MAAI,cAAc,GAAI,aAAY,OAAO,gBAAG,IAAIA,iBAAI,WAAW,KAAK,CAAC,KAAK,cAAc,KAAK;WACpF,cAAc,cAAc,SACpC,aAAY,OAAO,gBAAG,IAAIA,iBAAI,WAAW,aAAa,CAAC,KAAK,cAAc,aAAa;MACjF,aAAY,OAAO,gBAAG,IAAIA,iBAAI,WAAW,OAAO,CAAC,KAAK,cAAc,OAAO;AAElF,aAAW,KAAK,GAAG,QAAQ,WAAW,YAAY,cAAc,CAAC,CAAC,IAAI;;AAGvE,OAAM,SAAS,WAAW;GAG3B"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { UpgradeResult } from "./utils.cjs";
|
|
2
|
+
import { MigrationMeta } from "../migrator.cjs";
|
|
3
|
+
import { SqliteRemoteDatabase } from "../sqlite-proxy/index.cjs";
|
|
4
|
+
import { ProxyMigrator } from "../sqlite-proxy/migrator.cjs";
|
|
5
|
+
|
|
6
|
+
//#region src/up-migrations/sqlite-proxy.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Detects the current version of the migrations table schema and upgrades it if needed.
|
|
9
|
+
*
|
|
10
|
+
* Version 0: Original schema (id, hash, created_at)
|
|
11
|
+
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
12
|
+
*/
|
|
13
|
+
declare function upgradeAsyncIfNeeded(migrationsTable: string, db: SqliteRemoteDatabase<Record<string, unknown>>, callback: ProxyMigrator, localMigrations: MigrationMeta[]): Promise<UpgradeResult>;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { upgradeAsyncIfNeeded };
|
|
16
|
+
//# sourceMappingURL=sqlite-proxy.d.cts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { UpgradeResult } from "./utils.js";
|
|
2
|
+
import { MigrationMeta } from "../migrator.js";
|
|
3
|
+
import { SqliteRemoteDatabase } from "../sqlite-proxy/index.js";
|
|
4
|
+
import { ProxyMigrator } from "../sqlite-proxy/migrator.js";
|
|
5
|
+
|
|
6
|
+
//#region src/up-migrations/sqlite-proxy.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Detects the current version of the migrations table schema and upgrades it if needed.
|
|
9
|
+
*
|
|
10
|
+
* Version 0: Original schema (id, hash, created_at)
|
|
11
|
+
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
12
|
+
*/
|
|
13
|
+
declare function upgradeAsyncIfNeeded(migrationsTable: string, db: SqliteRemoteDatabase<Record<string, unknown>>, callback: ProxyMigrator, localMigrations: MigrationMeta[]): Promise<UpgradeResult>;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { upgradeAsyncIfNeeded };
|
|
16
|
+
//# sourceMappingURL=sqlite-proxy.d.ts.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS } from "./utils.js";
|
|
2
|
+
import { sql } from "../sql/sql.js";
|
|
3
|
+
|
|
4
|
+
//#region src/up-migrations/sqlite-proxy.ts
|
|
5
|
+
/**
|
|
6
|
+
* Detects the current version of the migrations table schema and upgrades it if needed.
|
|
7
|
+
*
|
|
8
|
+
* Version 0: Original schema (id, hash, created_at)
|
|
9
|
+
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
10
|
+
*/
|
|
11
|
+
async function upgradeAsyncIfNeeded(migrationsTable, db, callback, localMigrations) {
|
|
12
|
+
if ((await db.session.values(sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`)).length === 0) return { newDb: true };
|
|
13
|
+
const rows = await db.session.values(sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`);
|
|
14
|
+
const version = GET_VERSION_FOR.sqlite(rows.map((r) => r[0]));
|
|
15
|
+
for (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {
|
|
16
|
+
const upgradeFn = upgradeAsyncFunctions[v];
|
|
17
|
+
if (!upgradeFn) throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);
|
|
18
|
+
await upgradeFn(migrationsTable, db, callback, localMigrations);
|
|
19
|
+
}
|
|
20
|
+
return { newDb: false };
|
|
21
|
+
}
|
|
22
|
+
const upgradeAsyncFunctions = { 0: async (migrationsTable, db, callback, localMigrations) => {
|
|
23
|
+
const table = sql`${sql.identifier(migrationsTable)}`;
|
|
24
|
+
const dbRows = (await db.session.values(sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`)).map((row) => ({
|
|
25
|
+
id: row[0],
|
|
26
|
+
hash: row[1],
|
|
27
|
+
created_at: row[2]
|
|
28
|
+
}));
|
|
29
|
+
localMigrations.sort((a, b) => a.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? "").localeCompare(b.name ?? ""));
|
|
30
|
+
const byMillis = /* @__PURE__ */ new Map();
|
|
31
|
+
const byHash = /* @__PURE__ */ new Map();
|
|
32
|
+
for (const lm of localMigrations) {
|
|
33
|
+
if (!byMillis.has(lm.folderMillis)) byMillis.set(lm.folderMillis, []);
|
|
34
|
+
byMillis.get(lm.folderMillis).push(lm);
|
|
35
|
+
byHash.set(lm.hash, lm);
|
|
36
|
+
}
|
|
37
|
+
const toApply = [];
|
|
38
|
+
let unmatched = [];
|
|
39
|
+
for (const dbRow of dbRows) {
|
|
40
|
+
const stringified = String(dbRow.created_at);
|
|
41
|
+
const millis = Number(stringified.substring(0, stringified.length - 3) + "000");
|
|
42
|
+
const candidates = byMillis.get(millis);
|
|
43
|
+
let matched;
|
|
44
|
+
let matchedBy = null;
|
|
45
|
+
if (candidates && candidates.length === 1) {
|
|
46
|
+
matched = candidates[0];
|
|
47
|
+
matchedBy = "millis";
|
|
48
|
+
} else if (candidates && candidates.length > 1) {
|
|
49
|
+
matched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash);
|
|
50
|
+
if (matched) matchedBy = "hash";
|
|
51
|
+
} else {
|
|
52
|
+
matched = byHash.get(dbRow.hash);
|
|
53
|
+
if (matched) matchedBy = "hash";
|
|
54
|
+
}
|
|
55
|
+
if (matched) toApply.push({
|
|
56
|
+
id: dbRow.id,
|
|
57
|
+
name: matched.name,
|
|
58
|
+
hash: dbRow.hash,
|
|
59
|
+
created_at: stringified,
|
|
60
|
+
matchedBy: dbRow.id ? "id" : matchedBy
|
|
61
|
+
});
|
|
62
|
+
else unmatched.push(dbRow);
|
|
63
|
+
}
|
|
64
|
+
if (unmatched.length > 0) throw Error(`While upgrading your database migrations table we found ${unmatched.length} (${unmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(", ")}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`);
|
|
65
|
+
const statements = [db.dialect.sqlToQuery(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier("name")} text`.inlineParams()).sql, db.dialect.sqlToQuery(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier("applied_at")} TEXT`.inlineParams()).sql];
|
|
66
|
+
for (const backfillEntry of toApply) {
|
|
67
|
+
const updateQuery = sql`UPDATE ${table} SET ${sql.identifier("name")} = ${backfillEntry.name}, ${sql.identifier("applied_at")} = NULL WHERE`;
|
|
68
|
+
if (backfillEntry.id) updateQuery.append(sql` ${sql.identifier("id")} = ${backfillEntry.id}`);
|
|
69
|
+
else if (backfillEntry.matchedBy === "millis") updateQuery.append(sql` ${sql.identifier("created_at")} = ${backfillEntry.created_at}`);
|
|
70
|
+
else updateQuery.append(sql` ${sql.identifier("hash")} = ${backfillEntry.hash}`);
|
|
71
|
+
statements.push(db.dialect.sqlToQuery(updateQuery.inlineParams()).sql);
|
|
72
|
+
}
|
|
73
|
+
await callback(statements);
|
|
74
|
+
} };
|
|
75
|
+
|
|
76
|
+
//#endregion
|
|
77
|
+
export { upgradeAsyncIfNeeded };
|
|
78
|
+
//# sourceMappingURL=sqlite-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-proxy.js","names":[],"sources":["../../src/up-migrations/sqlite-proxy.ts"],"sourcesContent":["import type { MigrationMeta } from '~/migrator.ts';\nimport { sql } from '~/sql/sql.ts';\nimport type { BaseSQLiteDatabase } from '~/sqlite-core/index.ts';\nimport type { SqliteRemoteDatabase } from '~/sqlite-proxy/index.ts';\nimport type { ProxyMigrator } from '~/sqlite-proxy/migrator.ts';\nimport { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS, type UpgradeResult } from './utils.ts';\n\n/**\n * Detects the current version of the migrations table schema and upgrades it if needed.\n *\n * Version 0: Original schema (id, hash, created_at)\n * Version 1: Extended schema (id, hash, created_at, name, applied_at)\n */\nexport async function upgradeAsyncIfNeeded(\n\tmigrationsTable: string,\n\tdb: SqliteRemoteDatabase<Record<string, unknown>>,\n\tcallback: ProxyMigrator,\n\tlocalMigrations: MigrationMeta[],\n): Promise<UpgradeResult> {\n\t// Check if the table exists at all\n\tconst tableExists = await db.session.values<[1]>(\n\t\tsql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`,\n\t);\n\n\tif (tableExists.length === 0) {\n\t\treturn { newDb: true };\n\t}\n\n\t// Table exists, check table shape\n\tconst rows = await db.session.values<[string]>(\n\t\tsql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`,\n\t);\n\n\tconst version = GET_VERSION_FOR.sqlite(rows.map((r) => r[0]));\n\n\tfor (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {\n\t\tconst upgradeFn = upgradeAsyncFunctions[v];\n\t\tif (!upgradeFn) {\n\t\t\tthrow new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);\n\t\t}\n\t\tawait upgradeFn(migrationsTable, db, callback, localMigrations);\n\t}\n\n\treturn { newDb: false };\n}\n\nconst upgradeAsyncFunctions: Record<\n\tnumber,\n\t(\n\t\tmigrationsTable: string,\n\t\tdb: BaseSQLiteDatabase<'async', unknown, Record<string, unknown>>,\n\t\tcallback: ProxyMigrator,\n\t\tlocalMigrations: MigrationMeta[],\n\t) => Promise<void>\n> = {\n\t/**\n\t * Upgrade from version 0 to version 1:\n\t * 1. Read all existing DB migrations\n\t * 2. Sort localMigrations ASC by millis and if the same - sort by name\n\t * 3. Match each DB row to a local migration\n\t * If multiple migrations share the same second, use hash matching as a tiebreaker\n\t * Not implemented for now -> If hash matching fails, fall back to serial id ordering\n\t * 5. Create extra column and backfill names for matched migrations\n\t */\n\t0: async (migrationsTable, db, callback, localMigrations) => {\n\t\tconst table = sql`${sql.identifier(migrationsTable)}`;\n\n\t\t// 1. Read all existing DB migrations\n\t\t// Sort them by ids asc (order how they were applied)\n\t\t// this can be null from legacy implementation where id was serial\n\t\tconst dbRows = (await db.session.values<[number | null, string, number]>(\n\t\t\tsql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`,\n\t\t)).map((row) => ({\n\t\t\tid: row[0],\n\t\t\thash: row[1],\n\t\t\tcreated_at: row[2],\n\t\t}));\n\n\t\t// 2. Sort ASC by millis and if the same - sort by name\n\t\tlocalMigrations.sort((a, b) =>\n\t\t\ta.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? '').localeCompare(b.name ?? '')\n\t\t);\n\n\t\tconst byMillis = new Map<number, MigrationMeta[]>();\n\t\tconst byHash = new Map<string, MigrationMeta>();\n\t\tfor (const lm of localMigrations) {\n\t\t\tif (!byMillis.has(lm.folderMillis)) {\n\t\t\t\tbyMillis.set(lm.folderMillis, []);\n\t\t\t}\n\t\t\tbyMillis.get(lm.folderMillis)!.push(lm);\n\t\t\tbyHash.set(lm.hash, lm);\n\t\t}\n\n\t\t// \t3. Match each DB row to a local migration\n\t\t// \tPriority: millis -> hash\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\tconst toApply: {\n\t\t\tid: number | null;\n\t\t\tname: string;\n\t\t\thash: string;\n\t\t\tcreated_at: string;\n\t\t\tmatchedBy: 'id' | 'hash' | 'millis';\n\t\t}[] = [];\n\n\t\t// id can be null from legacy implementation where id was serial\n\t\t// hash can only be '' for bun-sqlite journal entries\n\t\tlet unmatched: { id: number | null; hash: string; created_at: number }[] = [];\n\n\t\tfor (const dbRow of dbRows) {\n\t\t\tconst stringified = String(dbRow.created_at);\n\t\t\tconst millis = Number(stringified.substring(0, stringified.length - 3) + '000');\n\t\t\tconst candidates = byMillis.get(millis);\n\n\t\t\tlet matched: MigrationMeta | undefined;\n\t\t\tlet matchedBy: 'hash' | 'millis' | null = null;\n\t\t\tif (candidates && candidates.length === 1) {\n\t\t\t\tmatched = candidates[0];\n\t\t\t\tmatchedBy = 'millis';\n\t\t\t} else if (candidates && candidates.length > 1) {\n\t\t\t\tmatched = candidates.find((c) => c.hash && dbRow.hash && c.hash === dbRow.hash); // for bun-sqlite cases (journal had empty hash)\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t} else {\n\t\t\t\tmatched = byHash.get(dbRow.hash);\n\t\t\t\tif (matched) matchedBy = 'hash';\n\t\t\t}\n\n\t\t\tif (matched) {\n\t\t\t\ttoApply.push({\n\t\t\t\t\tid: dbRow.id,\n\t\t\t\t\tname: matched.name,\n\t\t\t\t\thash: dbRow.hash,\n\t\t\t\t\tcreated_at: stringified,\n\t\t\t\t\tmatchedBy: dbRow.id ? 'id' : matchedBy!,\n\t\t\t\t});\n\t\t\t} else unmatched.push(dbRow);\n\t\t}\n\n\t\t// 4. Check for unmatched\n\t\t// Our assumption on this migration flow is that all DB entries should be matched to a local migration\n\t\t// (if same seconds - fallback to hash, if hash fails - corner case)\n\t\t// If there are unmatched entries, it means that the local environment is missing migrations that have been applied to the DB,\n\t\t// which can lead to inconsistencies and potential issues when running future migrations\n\t\tif (unmatched.length > 0) {\n\t\t\tthrow Error(\n\t\t\t\t`While upgrading your database migrations table we found ${unmatched.length} (${\n\t\t\t\t\tunmatched.map((it) => `[id: ${it.id}, created_at: ${it.created_at}]`).join(', ')\n\t\t\t\t}) migrations in the database that do not match any local migration. This means that some migrations were applied to the database but are missing from the local environment`,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Create extra column and backfill names for matched migrations\n\t\tconst statements: string[] = [\n\t\t\tdb.dialect.sqlToQuery(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('name')} text`.inlineParams()).sql,\n\t\t\tdb.dialect.sqlToQuery(sql`ALTER TABLE ${table} ADD COLUMN ${sql.identifier('applied_at')} TEXT`.inlineParams())\n\t\t\t\t.sql,\n\t\t];\n\t\tfor (const backfillEntry of toApply) {\n\t\t\tconst updateQuery = sql`UPDATE ${table} SET ${sql.identifier('name')} = ${backfillEntry.name}, ${\n\t\t\t\tsql.identifier('applied_at')\n\t\t\t} = NULL WHERE`;\n\n\t\t\t// id\n\t\t\t// created_at\n\t\t\t// hash\n\t\t\tif (backfillEntry.id) updateQuery.append(sql` ${sql.identifier('id')} = ${backfillEntry.id}`);\n\t\t\telse if (backfillEntry.matchedBy === 'millis') {\n\t\t\t\tupdateQuery.append(sql` ${sql.identifier('created_at')} = ${backfillEntry.created_at}`);\n\t\t\t} else updateQuery.append(sql` ${sql.identifier('hash')} = ${backfillEntry.hash}`);\n\n\t\t\tstatements.push(db.dialect.sqlToQuery(updateQuery.inlineParams()).sql);\n\t\t}\n\n\t\tawait callback(statements);\n\t\treturn;\n\t},\n};\n"],"mappings":"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"}
|
package/up-migrations/sqlite.cjs
CHANGED
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
|
|
3
|
-
|
|
3
|
+
const require_up_migrations_utils = require('./utils.cjs');
|
|
4
4
|
let __sql_sql_ts = require("../sql/sql.cjs");
|
|
5
5
|
|
|
6
6
|
//#region src/up-migrations/sqlite.ts
|
|
7
|
-
const CURRENT_MIGRATION_TABLE_VERSION = 1;
|
|
8
|
-
function getVersion(columns) {
|
|
9
|
-
if (columns.includes("name")) return 1;
|
|
10
|
-
return 0;
|
|
11
|
-
}
|
|
12
|
-
function allSync(session, sqlQuery, resultMapper = () => []) {
|
|
13
|
-
const result = session.all(sqlQuery);
|
|
14
|
-
if (result.length === 0) return [];
|
|
15
|
-
if (Array.isArray(result[0])) return result.map((row) => resultMapper(row));
|
|
16
|
-
return result;
|
|
17
|
-
}
|
|
18
|
-
async function allAsync(session, sqlQuery, resultMapper = () => []) {
|
|
19
|
-
const result = await session.all(sqlQuery);
|
|
20
|
-
if (result.length === 0) return [];
|
|
21
|
-
if (Array.isArray(result[0])) return result.map((row) => resultMapper(row));
|
|
22
|
-
return result;
|
|
23
|
-
}
|
|
24
7
|
/**
|
|
25
8
|
* Detects the current version of the migrations table schema and upgrades it if needed.
|
|
26
9
|
*
|
|
@@ -28,25 +11,19 @@ async function allAsync(session, sqlQuery, resultMapper = () => []) {
|
|
|
28
11
|
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
29
12
|
*/
|
|
30
13
|
function upgradeSyncIfNeeded(migrationsTable, session, localMigrations) {
|
|
31
|
-
if (
|
|
32
|
-
const
|
|
33
|
-
|
|
14
|
+
if (session.all(__sql_sql_ts.sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`).length === 0) return { newDb: true };
|
|
15
|
+
const rows = session.all(__sql_sql_ts.sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`);
|
|
16
|
+
const version = require_up_migrations_utils.GET_VERSION_FOR.sqlite(rows.map((r) => r.column_name));
|
|
17
|
+
for (let v = version; v < require_up_migrations_utils.MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {
|
|
34
18
|
const upgradeFn = upgradeSyncFunctions[v];
|
|
35
19
|
if (!upgradeFn) throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);
|
|
36
20
|
upgradeFn(migrationsTable, session, localMigrations);
|
|
37
21
|
}
|
|
38
|
-
return {
|
|
39
|
-
prevVersion: version,
|
|
40
|
-
currentVersion: CURRENT_MIGRATION_TABLE_VERSION
|
|
41
|
-
};
|
|
22
|
+
return { newDb: false };
|
|
42
23
|
}
|
|
43
24
|
const upgradeSyncFunctions = { 0: (migrationsTable, session, localMigrations) => {
|
|
44
25
|
const table = __sql_sql_ts.sql`${__sql_sql_ts.sql.identifier(migrationsTable)}`;
|
|
45
|
-
const dbRows =
|
|
46
|
-
id: row[0],
|
|
47
|
-
hash: row[1],
|
|
48
|
-
created_at: row[2]
|
|
49
|
-
}));
|
|
26
|
+
const dbRows = session.all(__sql_sql_ts.sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`);
|
|
50
27
|
localMigrations.sort((a, b) => a.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? "").localeCompare(b.name ?? ""));
|
|
51
28
|
const byMillis = /* @__PURE__ */ new Map();
|
|
52
29
|
const byHash = /* @__PURE__ */ new Map();
|
|
@@ -101,26 +78,20 @@ const upgradeSyncFunctions = { 0: (migrationsTable, session, localMigrations) =>
|
|
|
101
78
|
* Version 0: Original schema (id, hash, created_at)
|
|
102
79
|
* Version 1: Extended schema (id, hash, created_at, name, applied_at)
|
|
103
80
|
*/
|
|
104
|
-
async function upgradeAsyncIfNeeded(migrationsTable,
|
|
105
|
-
if ((await
|
|
106
|
-
const
|
|
107
|
-
|
|
81
|
+
async function upgradeAsyncIfNeeded(migrationsTable, db, localMigrations) {
|
|
82
|
+
if ((await db.session.all(__sql_sql_ts.sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`)).length === 0) return { newDb: true };
|
|
83
|
+
const rows = await db.session.all(__sql_sql_ts.sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`);
|
|
84
|
+
const version = require_up_migrations_utils.GET_VERSION_FOR.sqlite(rows.map((r) => r.column_name));
|
|
85
|
+
for (let v = version; v < require_up_migrations_utils.MIGRATIONS_TABLE_VERSIONS.sqlite; v++) {
|
|
108
86
|
const upgradeFn = upgradeAsyncFunctions[v];
|
|
109
87
|
if (!upgradeFn) throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`);
|
|
110
|
-
await upgradeFn(migrationsTable,
|
|
88
|
+
await upgradeFn(migrationsTable, db, localMigrations);
|
|
111
89
|
}
|
|
112
|
-
return {
|
|
113
|
-
prevVersion: version,
|
|
114
|
-
currentVersion: CURRENT_MIGRATION_TABLE_VERSION
|
|
115
|
-
};
|
|
90
|
+
return { newDb: false };
|
|
116
91
|
}
|
|
117
|
-
const upgradeAsyncFunctions = { 0: async (migrationsTable,
|
|
92
|
+
const upgradeAsyncFunctions = { 0: async (migrationsTable, db, localMigrations) => {
|
|
118
93
|
const table = __sql_sql_ts.sql`${__sql_sql_ts.sql.identifier(migrationsTable)}`;
|
|
119
|
-
const dbRows = await
|
|
120
|
-
id: row[0],
|
|
121
|
-
hash: row[1],
|
|
122
|
-
created_at: row[2]
|
|
123
|
-
}));
|
|
94
|
+
const dbRows = await db.session.all(__sql_sql_ts.sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`);
|
|
124
95
|
localMigrations.sort((a, b) => a.folderMillis !== b.folderMillis ? a.folderMillis - b.folderMillis : (a.name ?? "").localeCompare(b.name ?? ""));
|
|
125
96
|
const byMillis = /* @__PURE__ */ new Map();
|
|
126
97
|
const byHash = /* @__PURE__ */ new Map();
|
|
@@ -165,11 +136,9 @@ const upgradeAsyncFunctions = { 0: async (migrationsTable, session, localMigrati
|
|
|
165
136
|
else updateQuery.append(__sql_sql_ts.sql` ${__sql_sql_ts.sql.identifier("hash")} = ${backfillEntry.hash}`);
|
|
166
137
|
statements.push(updateQuery);
|
|
167
138
|
}
|
|
168
|
-
|
|
139
|
+
await db.transaction(async (tx) => {
|
|
169
140
|
for (const statement of statements) await tx.run(statement);
|
|
170
141
|
});
|
|
171
|
-
else if (mode === "run") for (const statement of statements) await session.run(statement);
|
|
172
|
-
else (0, __utils_ts.assertUnreachable)(mode);
|
|
173
142
|
} };
|
|
174
143
|
|
|
175
144
|
//#endregion
|