diff --git a/packages/orm/src/client/client-impl.ts b/packages/orm/src/client/client-impl.ts index 439735d49..dff7f9c27 100644 --- a/packages/orm/src/client/client-impl.ts +++ b/packages/orm/src/client/client-impl.ts @@ -128,7 +128,14 @@ export class ClientImpl { }); } - this.kysely = new Kysely(this.kyselyProps); + if (baseClient?.isTransaction && !executor) { + // if we're creating a derived client from a transaction client and not replacing + // the executor, reuse the current kysely instance to retain the transaction context + this.kysely = baseClient.$qb; + } else { + this.kysely = new Kysely(this.kyselyProps); + } + this.inputValidator = baseClient?.inputValidator ?? new InputValidator(this as any, { enabled: this.$options.validateInput !== false }); @@ -956,12 +963,7 @@ function collectExtResultFieldDefs( * - Injects `needs` fields into `select` when ext result fields are explicitly selected * - Recurses into `include` and `select` for nested relation fields */ -function prepareArgsForExtResult( - args: unknown, - model: string, - schema: SchemaDef, - plugins: AnyPlugin[], -): unknown { +function prepareArgsForExtResult(args: unknown, model: string, schema: SchemaDef, plugins: AnyPlugin[]): unknown { if (!args || typeof args !== 'object') { return args; } diff --git a/tests/e2e/orm/client-api/transaction.test.ts b/tests/e2e/orm/client-api/transaction.test.ts index e4f2192e8..84e267aad 100644 --- a/tests/e2e/orm/client-api/transaction.test.ts +++ b/tests/e2e/orm/client-api/transaction.test.ts @@ -101,6 +101,77 @@ describe('Client raw query tests', () => { await expect(client.user.findMany()).toResolveWithLength(0); }); + + it('$unuseAll preserves transaction isolation', async () => { + await expect( + client.$transaction(async (tx) => { + await tx.$unuseAll().user.create({ + data: { email: 'u1@test.com' }, + }); + throw new Error('rollback'); + }), + ).rejects.toThrow('rollback'); + + await expect(client.user.findMany()).toResolveWithLength(0); + }); + + it('$unuse preserves transaction isolation', async () => { + await expect( + client.$transaction(async (tx) => { + await tx.$unuse('nonexistent').user.create({ + data: { email: 'u1@test.com' }, + }); + throw new Error('rollback'); + }), + ).rejects.toThrow('rollback'); + + await expect(client.user.findMany()).toResolveWithLength(0); + }); + + it('$use preserves transaction isolation', async () => { + await expect( + client.$transaction(async (tx) => { + await (tx as any) + .$use({ + id: 'noop', + onQuery: async ({ args, proceed }: { args: unknown; proceed: (args: unknown) => Promise }) => + proceed(args), + }) + .user.create({ + data: { email: 'u1@test.com' }, + }); + throw new Error('rollback'); + }), + ).rejects.toThrow('rollback'); + + await expect(client.user.findMany()).toResolveWithLength(0); + }); + + it('$setAuth preserves transaction isolation', async () => { + await expect( + client.$transaction(async (tx) => { + await tx.$setAuth(undefined).user.create({ + data: { email: 'u1@test.com' }, + }); + throw new Error('rollback'); + }), + ).rejects.toThrow('rollback'); + + await expect(client.user.findMany()).toResolveWithLength(0); + }); + + it('$setOptions preserves transaction isolation', async () => { + await expect( + client.$transaction(async (tx) => { + await (tx as any).$setOptions((tx as any).$options).user.create({ + data: { email: 'u1@test.com' }, + }); + throw new Error('rollback'); + }), + ).rejects.toThrow('rollback'); + + await expect(client.user.findMany()).toResolveWithLength(0); + }); }); describe('sequential transaction', () => {