|
1 | 1 | /* oxlint-disable */ |
2 | | -import type { TablesRelationalConfig } from "drizzle-orm/_relations" |
3 | 2 | import type { MigrationMeta } from "drizzle-orm/migrator" |
4 | | -import type { AnyRelations } from "drizzle-orm/relations" |
5 | 3 | import { type SQL, sql } from "drizzle-orm/sql/sql" |
6 | | -import type { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core" |
7 | | -import type { SQLiteSession } from "drizzle-orm/sqlite-core/session" |
8 | | -import { GET_VERSION_FOR, MIGRATIONS_TABLE_VERSIONS, type UpgradeResult } from "./utils.js" |
9 | 4 |
|
10 | 5 | /** @internal */ |
11 | 6 | export type SQLiteMigrationTableRow = { id: number | null; hash: string; created_at: number } |
12 | 7 |
|
13 | | -type AsyncSQLiteDatabaseWithSession = BaseSQLiteDatabase<"async", unknown, Record<string, unknown>> & { |
14 | | - session: { |
15 | | - all<T>(query: SQL): Promise<T[]> |
16 | | - } |
17 | | - transaction<T>(transaction: (tx: { run(query: SQL): Promise<unknown> }) => Promise<T>): Promise<T> |
18 | | -} |
19 | | - |
20 | 8 | type SQLiteMigrationBackfillEntry = { |
21 | 9 | name: string |
22 | 10 | selector: |
@@ -115,139 +103,3 @@ export function buildSQLiteMigrationBackfillStatements( |
115 | 103 |
|
116 | 104 | return statements |
117 | 105 | } |
118 | | - |
119 | | -/** |
120 | | - * Detects the current version of the migrations table schema and upgrades it if needed. |
121 | | - * |
122 | | - * Version 0: Original schema (id, hash, created_at) |
123 | | - * Version 1: Extended schema (id, hash, created_at, name, applied_at) |
124 | | - */ |
125 | | -export function upgradeSyncIfNeeded( |
126 | | - migrationsTable: string, |
127 | | - session: SQLiteSession<"sync", unknown, Record<string, unknown>, AnyRelations, TablesRelationalConfig>, |
128 | | - localMigrations: MigrationMeta[], |
129 | | -): UpgradeResult { |
130 | | - const tableExists = session.all(sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`) |
131 | | - |
132 | | - if (tableExists.length === 0) { |
133 | | - return { newDb: true } |
134 | | - } |
135 | | - |
136 | | - // Table exists, check table shape |
137 | | - const rows = session.all<{ column_name: string }>( |
138 | | - sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`, |
139 | | - ) |
140 | | - |
141 | | - const version = GET_VERSION_FOR.sqlite(rows.map((r) => r.column_name)) |
142 | | - |
143 | | - for (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) { |
144 | | - const upgradeFn = upgradeSyncFunctions[v] |
145 | | - if (!upgradeFn) { |
146 | | - throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`) |
147 | | - } |
148 | | - upgradeFn(migrationsTable, session, localMigrations) |
149 | | - } |
150 | | - |
151 | | - return { newDb: false } |
152 | | -} |
153 | | - |
154 | | -const upgradeSyncFunctions: Record< |
155 | | - number, |
156 | | - ( |
157 | | - migrationsTable: string, |
158 | | - session: SQLiteSession<"sync", unknown, Record<string, unknown>, AnyRelations, TablesRelationalConfig>, |
159 | | - localMigrations: MigrationMeta[], |
160 | | - ) => void |
161 | | -> = { |
162 | | - /** |
163 | | - * Upgrade from version 0 to version 1: |
164 | | - * 1. Read all existing DB migrations |
165 | | - * 2. Sort localMigrations ASC by millis and if the same - sort by name |
166 | | - * 3. Match each DB row to a local migration |
167 | | - * If multiple migrations share the same second, use hash matching as a tiebreaker |
168 | | - * Not implemented for now -> If hash matching fails, fall back to serial id ordering |
169 | | - * 5. Create extra column and backfill names for matched migrations |
170 | | - */ |
171 | | - 0: (migrationsTable, session, localMigrations) => { |
172 | | - const table = sql`${sql.identifier(migrationsTable)}` |
173 | | - const dbRows = session.all<SQLiteMigrationTableRow>(sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`) |
174 | | - const statements = buildSQLiteMigrationBackfillStatements( |
175 | | - migrationsTable, |
176 | | - prepareSQLiteMigrationBackfill(dbRows, localMigrations), |
177 | | - ) |
178 | | - |
179 | | - session.transaction((tx) => { |
180 | | - for (const statement of statements) { |
181 | | - tx.run(statement) |
182 | | - } |
183 | | - }) |
184 | | - }, |
185 | | -} |
186 | | - |
187 | | -/** |
188 | | - * Detects the current version of the migrations table schema and upgrades it if needed. |
189 | | - * |
190 | | - * Version 0: Original schema (id, hash, created_at) |
191 | | - * Version 1: Extended schema (id, hash, created_at, name, applied_at) |
192 | | - */ |
193 | | -export async function upgradeAsyncIfNeeded( |
194 | | - migrationsTable: string, |
195 | | - db: AsyncSQLiteDatabaseWithSession, |
196 | | - localMigrations: MigrationMeta[], |
197 | | -): Promise<UpgradeResult> { |
198 | | - // Check if the table exists at all |
199 | | - const tableExists = await db.session.all( |
200 | | - sql`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${migrationsTable}`, |
201 | | - ) |
202 | | - |
203 | | - if (tableExists.length === 0) { |
204 | | - return { newDb: true } |
205 | | - } |
206 | | - |
207 | | - const rows = await db.session.all<{ column_name: string }>( |
208 | | - sql`SELECT name as column_name FROM pragma_table_info(${migrationsTable})`, |
209 | | - ) |
210 | | - |
211 | | - const version = GET_VERSION_FOR.sqlite(rows.map((r) => r.column_name)) |
212 | | - |
213 | | - for (let v = version; v < MIGRATIONS_TABLE_VERSIONS.sqlite; v++) { |
214 | | - const upgradeFn = upgradeAsyncFunctions[v] |
215 | | - if (!upgradeFn) { |
216 | | - throw new Error(`No upgrade path from migration table version ${v} to ${v + 1}`) |
217 | | - } |
218 | | - await upgradeFn(migrationsTable, db, localMigrations) |
219 | | - } |
220 | | - |
221 | | - return { newDb: false } |
222 | | -} |
223 | | - |
224 | | -const upgradeAsyncFunctions: Record< |
225 | | - number, |
226 | | - (migrationsTable: string, db: AsyncSQLiteDatabaseWithSession, localMigrations: MigrationMeta[]) => Promise<void> |
227 | | -> = { |
228 | | - /** |
229 | | - * Upgrade from version 0 to version 1: |
230 | | - * 1. Read all existing DB migrations |
231 | | - * 2. Sort localMigrations ASC by millis and if the same - sort by name |
232 | | - * 3. Match each DB row to a local migration |
233 | | - * If multiple migrations share the same second, use hash matching as a tiebreaker |
234 | | - * Not implemented for now -> If hash matching fails, fall back to serial id ordering |
235 | | - * 5. Create extra column and backfill names for matched migrations |
236 | | - */ |
237 | | - 0: async (migrationsTable, db, localMigrations) => { |
238 | | - const table = sql`${sql.identifier(migrationsTable)}` |
239 | | - const dbRows = await db.session.all<SQLiteMigrationTableRow>( |
240 | | - sql`SELECT id, hash, created_at FROM ${table} ORDER BY id ASC`, |
241 | | - ) |
242 | | - const statements = buildSQLiteMigrationBackfillStatements( |
243 | | - migrationsTable, |
244 | | - prepareSQLiteMigrationBackfill(dbRows, localMigrations), |
245 | | - ) |
246 | | - |
247 | | - await db.transaction(async (tx) => { |
248 | | - for (const statement of statements) { |
249 | | - await tx.run(statement) |
250 | | - } |
251 | | - }) |
252 | | - }, |
253 | | -} |
0 commit comments