Skip to content

Commit dc89b9e

Browse files
committed
feat(migrator): allow disabling transactions in migrate methods. (#1517)
1 parent 96202e8 commit dc89b9e

File tree

2 files changed

+79
-27
lines changed

2 files changed

+79
-27
lines changed

src/migration/migrator.ts

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ export class Migrator {
146146
* }
147147
* ```
148148
*/
149-
async migrateToLatest(): Promise<MigrationResultSet> {
150-
return this.#migrate(() => ({ direction: 'Up', step: Infinity }))
149+
async migrateToLatest(options?: MigrateOptions): Promise<MigrationResultSet> {
150+
return this.#migrate(() => ({ direction: 'Up', step: Infinity }), options)
151151
}
152152

153153
/**
@@ -203,6 +203,7 @@ export class Migrator {
203203
*/
204204
async migrateTo(
205205
targetMigrationName: string | NoMigrations,
206+
options?: MigrateOptions,
206207
): Promise<MigrationResultSet> {
207208
return this.#migrate(
208209
({
@@ -226,6 +227,7 @@ export class Migrator {
226227
const executedIndex = executedMigrations.indexOf(
227228
targetMigrationName as string,
228229
)
230+
229231
const pendingIndex = pendingMigrations.findIndex(
230232
(m) => m.name === (targetMigrationName as string),
231233
)
@@ -235,14 +237,17 @@ export class Migrator {
235237
direction: 'Down',
236238
step: executedMigrations.length - executedIndex - 1,
237239
}
238-
} else if (pendingIndex !== -1) {
240+
}
241+
242+
if (pendingIndex !== -1) {
239243
return { direction: 'Up', step: pendingIndex + 1 }
240-
} else {
241-
throw new Error(
242-
`migration "${targetMigrationName}" isn't executed or pending`,
243-
)
244244
}
245+
246+
throw new Error(
247+
`migration "${targetMigrationName}" isn't executed or pending`,
248+
)
245249
},
250+
options,
246251
)
247252
}
248253

@@ -274,8 +279,8 @@ export class Migrator {
274279
* await migrator.migrateUp()
275280
* ```
276281
*/
277-
async migrateUp(): Promise<MigrationResultSet> {
278-
return this.#migrate(() => ({ direction: 'Up', step: 1 }))
282+
async migrateUp(options?: MigrateOptions): Promise<MigrationResultSet> {
283+
return this.#migrate(() => ({ direction: 'Up', step: 1 }), options)
279284
}
280285

281286
/**
@@ -306,19 +311,20 @@ export class Migrator {
306311
* await migrator.migrateDown()
307312
* ```
308313
*/
309-
async migrateDown(): Promise<MigrationResultSet> {
310-
return this.#migrate(() => ({ direction: 'Down', step: 1 }))
314+
async migrateDown(options?: MigrateOptions): Promise<MigrationResultSet> {
315+
return this.#migrate(() => ({ direction: 'Down', step: 1 }), options)
311316
}
312317

313318
async #migrate(
314319
getMigrationDirectionAndStep: (state: MigrationState) => {
315320
direction: MigrationDirection
316321
step: number
317322
},
323+
options: MigrateOptions | undefined,
318324
): Promise<MigrationResultSet> {
319325
try {
320326
await this.#ensureMigrationTablesExists()
321-
return await this.#runMigrations(getMigrationDirectionAndStep)
327+
return await this.#runMigrations(getMigrationDirectionAndStep, options)
322328
} catch (error) {
323329
if (error instanceof MigrationResultSetError) {
324330
return error.resultSet
@@ -489,6 +495,7 @@ export class Migrator {
489495
direction: MigrationDirection
490496
step: number
491497
},
498+
options: MigrateOptions | undefined,
492499
): Promise<MigrationResultSet> {
493500
const adapter = this.#props.db.getExecutor().adapter
494501

@@ -526,11 +533,14 @@ export class Migrator {
526533
}
527534
}
528535

529-
if (adapter.supportsTransactionalDdl && !this.#props.disableTransactions) {
530-
return this.#props.db.transaction().execute(run)
531-
} else {
536+
const disableTransaction =
537+
options?.disableTransactions ?? this.#props.disableTransactions
538+
539+
if (!adapter.supportsTransactionalDdl || disableTransaction) {
532540
return this.#props.db.connection().execute(run)
533541
}
542+
543+
return this.#props.db.transaction().execute(run)
534544
}
535545

536546
async #getState(db: Kysely<any>): Promise<MigrationState> {
@@ -752,7 +762,18 @@ export class Migrator {
752762
}
753763
}
754764

755-
export interface MigratorProps {
765+
export interface MigrateOptions {
766+
/**
767+
* When `true`, don't run migrations in transactions even if the dialect supports transactional DDL.
768+
*
769+
* Default is `false`.
770+
*
771+
* This is useful when some migrations include queries that would fail otherwise.
772+
*/
773+
readonly disableTransactions?: boolean
774+
}
775+
776+
export interface MigratorProps extends MigrateOptions {
756777
readonly db: Kysely<any>
757778
readonly provider: MigrationProvider
758779

@@ -825,15 +846,6 @@ export interface MigratorProps {
825846
* Default is `name0.localeCompare(name1)`.
826847
*/
827848
readonly nameComparator?: (name0: string, name1: string) => number
828-
829-
/**
830-
* When `true`, don't run migrations in transactions even if the dialect supports transactional DDL.
831-
*
832-
* Default is `false`.
833-
*
834-
* This is useful when some migrations include queries that would fail otherwise.
835-
*/
836-
readonly disableTransactions?: boolean
837849
}
838850

839851
/**

test/node/src/migration.test.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ for (const dialect of DIALECTS) {
791791
expect(executedUpMethods2).to.eql(['migration2', 'migration4'])
792792
})
793793

794-
it('should not execute in transaction if disableTransactions is true', async () => {
794+
it('should not execute in transaction if disableTransactions is true on the `Migrator` instance', async () => {
795795
const [migrator, executedUpMethods] = createMigrations(['migration1'], {
796796
disableTransactions: true,
797797
})
@@ -807,7 +807,25 @@ for (const dialect of DIALECTS) {
807807
expect(transactionSpy.called).to.be.false
808808
})
809809

810-
it('should execute in transaction if disableTransactions is false and transactionDdl supported', async () => {
810+
it('should not execute in transaction if disableTransactions is true when calling `migrateUp`', async () => {
811+
const [migrator, executedUpMethods] = createMigrations(['migration1'], {
812+
disableTransactions: false,
813+
})
814+
815+
const { results } = await migrator.migrateUp({
816+
disableTransactions: true,
817+
})
818+
819+
expect(results).to.eql([
820+
{ migrationName: 'migration1', direction: 'Up', status: 'Success' },
821+
])
822+
823+
expect(executedUpMethods).to.eql(['migration1'])
824+
825+
expect(transactionSpy.called).to.be.false
826+
})
827+
828+
it('should execute in transaction if disableTransactions is false on the `Migrator` instance and transactionDdl supported', async () => {
811829
const [migrator, executedUpMethods] = createMigrations(['migration1'], {
812830
disableTransactions: false,
813831
})
@@ -826,6 +844,28 @@ for (const dialect of DIALECTS) {
826844
expect(transactionSpy.called).to.be.false
827845
}
828846
})
847+
848+
it('should execute in transaction if disableTransactions is false when calling `migrateUp` and transactionDdl supported', async () => {
849+
const [migrator, executedUpMethods] = createMigrations(['migration1'], {
850+
disableTransactions: true,
851+
})
852+
853+
const { results } = await migrator.migrateUp({
854+
disableTransactions: false,
855+
})
856+
857+
expect(results).to.eql([
858+
{ migrationName: 'migration1', direction: 'Up', status: 'Success' },
859+
])
860+
861+
expect(executedUpMethods).to.eql(['migration1'])
862+
863+
if (ctx.db.getExecutor().adapter.supportsTransactionalDdl) {
864+
expect(transactionSpy.called).to.be.true
865+
} else {
866+
expect(transactionSpy.called).to.be.false
867+
}
868+
})
829869
})
830870

831871
describe('migrateDown', () => {

0 commit comments

Comments
 (0)