Skip to content

Commit 06eca6f

Browse files
feat(Migrator): allow passing transactions to Migrator. (#1480)
Co-authored-by: Igal Klebanov <igalklebanov@gmail.com> Co-authored-by: João Lucas de Oliveira Lopes <55464917+jlucaso1@users.noreply.github.com>
1 parent fd86b66 commit 06eca6f

File tree

2 files changed

+93
-10
lines changed

2 files changed

+93
-10
lines changed

src/migration/migrator.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -533,14 +533,30 @@ export class Migrator {
533533
}
534534
}
535535

536-
const disableTransaction =
536+
const disableTransactions =
537537
options?.disableTransactions ?? this.#props.disableTransactions
538538

539-
if (!adapter.supportsTransactionalDdl || disableTransaction) {
540-
return this.#props.db.connection().execute(run)
539+
if (this.#props.db.isTransaction) {
540+
if (!adapter.supportsTransactionalDdl) {
541+
throw new Error(
542+
'Transactional DDL is not supported in this dialect. Passing a transaction to this migrator would result in failure or unexpected behavior.',
543+
)
544+
}
545+
546+
if (disableTransactions) {
547+
throw new Error(
548+
'`disableTransactions` is true but the migrator was given a transaction. Passing a transaction to this migrator would result in failure or unexpected behavior.',
549+
)
550+
}
551+
552+
return run(this.#props.db)
553+
}
554+
555+
if (adapter.supportsTransactionalDdl && !disableTransactions) {
556+
return this.#props.db.transaction().execute(run)
541557
}
542558

543-
return this.#props.db.transaction().execute(run)
559+
return this.#props.db.connection().execute(run)
544560
}
545561

546562
async #getState(db: Kysely<any>): Promise<MigrationState> {

test/node/src/migration.test.ts

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import * as path from 'path'
2-
import { promises as fs } from 'fs'
1+
import * as path from 'node:path'
2+
import { promises as fs } from 'node:fs'
33

44
import {
55
FileMigrationProvider,
6-
Migration,
7-
MigrationResultSet,
6+
type Migration,
7+
type MigrationResultSet,
88
DEFAULT_MIGRATION_LOCK_TABLE,
99
DEFAULT_MIGRATION_TABLE,
10+
type MigrationProvider,
1011
Migrator,
1112
NO_MIGRATIONS,
12-
MigratorProps,
13+
type MigratorProps,
1314
type Kysely,
1415
} from '../../../'
1516

@@ -18,7 +19,7 @@ import {
1819
destroyTest,
1920
expect,
2021
initTest,
21-
TestContext,
22+
type TestContext,
2223
DIALECTS,
2324
type Database,
2425
} from './test-setup.js'
@@ -866,6 +867,72 @@ for (const dialect of DIALECTS) {
866867
expect(transactionSpy.called).to.be.false
867868
}
868869
})
870+
871+
if (sqlSpec === 'postgres' || sqlSpec === 'mssql') {
872+
it('should run migrations using a provided transaction', async () => {
873+
const migrationName = 'trx_connection_method_fail_case'
874+
875+
const trx = await ctx.db.startTransaction().execute()
876+
877+
try {
878+
const provider: MigrationProvider = {
879+
getMigrations: async () => ({
880+
[migrationName]: {
881+
async up(db: Kysely<any>): Promise<void> {
882+
expect(db).to.equal(trx)
883+
},
884+
},
885+
}),
886+
}
887+
888+
const migrator = new Migrator({ db: trx, provider })
889+
890+
const { results, error } = await migrator.migrateUp()
891+
892+
expect(trx.isCommitted).to.be.false
893+
expect(trx.isRolledBack).to.be.false
894+
895+
expect(error).to.be.undefined
896+
expect(results).to.eql([
897+
{ migrationName, direction: 'Up', status: 'Success' },
898+
])
899+
} finally {
900+
await trx.rollback().execute()
901+
}
902+
})
903+
}
904+
905+
if (sqlSpec === 'mysql' || sqlSpec === 'sqlite') {
906+
it('should refuse to run migrations using a provided transaction due to lack of support for transactional DDL', async () => {
907+
const trx = await ctx.db.startTransaction().execute()
908+
try {
909+
const provider: MigrationProvider = {
910+
getMigrations: async () => ({
911+
some_migration: {
912+
async up(db: Kysely<any>): Promise<void> {
913+
expect(db).to.equal(trx)
914+
},
915+
},
916+
}),
917+
}
918+
919+
const migrator = new Migrator({ db: trx, provider })
920+
921+
const { results, error } = await migrator.migrateUp()
922+
923+
expect(error).to.be.an.instanceOf(Error)
924+
expect(getMessage(error)).to.eql(
925+
'Transactional DDL is not supported in this dialect. Passing a transaction to this migrator would result in failure or unexpected behavior.',
926+
)
927+
expect(results).to.be.undefined
928+
929+
expect(trx.isCommitted).to.be.false
930+
expect(trx.isRolledBack).to.be.false
931+
} finally {
932+
await trx.rollback().execute()
933+
}
934+
})
935+
}
869936
})
870937

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

0 commit comments

Comments
 (0)