From f63327e19b32bf67367d74c798808e8e8c5b0cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20Koskim=C3=A4ki?= Date: Sun, 16 Jun 2024 08:58:55 +0300 Subject: [PATCH] closes #1021 --- src/query-builder/update-query-builder.ts | 98 ++++++++++--------- test/node/src/delete.test.ts | 29 ++++++ test/node/src/object-util.test.ts | 30 ++++++ test/node/src/update.test.ts | 28 ++++++ .../test-d/delete-query-builder.test-d.ts | 9 ++ test/typings/test-d/infer-result.test-d.ts | 20 +++- test/typings/test-d/update.test-d.ts | 16 +++ 7 files changed, 183 insertions(+), 47 deletions(-) create mode 100644 test/node/src/object-util.test.ts diff --git a/src/query-builder/update-query-builder.ts b/src/query-builder/update-query-builder.ts index f8422bfba..cbe670461 100644 --- a/src/query-builder/update-query-builder.ts +++ b/src/query-builder/update-query-builder.ts @@ -682,18 +682,28 @@ export class UpdateQueryBuilder }) } - returningAll(): UpdateQueryBuilder> { + returningAll( + tables: ReadonlyArray, + ): UpdateQueryBuilder> + + returningAll( + table: T, + ): UpdateQueryBuilder> + + returningAll(): UpdateQueryBuilder> + + returningAll(table?: any): any { return new UpdateQueryBuilder({ ...this.#props, queryNode: QueryNode.cloneWithReturning( this.#props.queryNode, - parseSelectAll(), + parseSelectAll(table), ), }) } output>( - selections: readonly OE[] + selections: readonly OE[], ): UpdateQueryBuilder< DB, UT, @@ -702,7 +712,7 @@ export class UpdateQueryBuilder > output>( - callback: CB + callback: CB, ): UpdateQueryBuilder< DB, UT, @@ -711,7 +721,7 @@ export class UpdateQueryBuilder > output>( - selection: OE + selection: OE, ): UpdateQueryBuilder< DB, UT, @@ -724,19 +734,19 @@ export class UpdateQueryBuilder ...this.#props, queryNode: QueryNode.cloneWithOutput( this.#props.queryNode, - parseSelectArg(args) + parseSelectArg(args), ), }) } outputAll( - table: OutputPrefix + table: OutputPrefix, ): UpdateQueryBuilder> { return new UpdateQueryBuilder({ ...this.#props, queryNode: QueryNode.cloneWithOutput( this.#props.queryNode, - parseSelectAll(table) + parseSelectAll(table), ), }) } @@ -836,8 +846,8 @@ export class UpdateQueryBuilder ): O2 extends UpdateResult ? UpdateQueryBuilder : O2 extends O & infer E - ? UpdateQueryBuilder> - : UpdateQueryBuilder> { + ? UpdateQueryBuilder> + : UpdateQueryBuilder> { if (condition) { return func(this) as any } @@ -1099,12 +1109,12 @@ export type UpdateQueryBuilderWithInnerJoin< ? InnerJoinedBuilder : never : TE extends keyof DB - ? UpdateQueryBuilder - : TE extends AliasedExpression - ? InnerJoinedBuilder - : TE extends (qb: any) => AliasedExpression - ? InnerJoinedBuilder - : never + ? UpdateQueryBuilder + : TE extends AliasedExpression + ? InnerJoinedBuilder + : TE extends (qb: any) => AliasedExpression + ? InnerJoinedBuilder + : never type InnerJoinedBuilder< DB, @@ -1133,12 +1143,12 @@ export type UpdateQueryBuilderWithLeftJoin< ? LeftJoinedBuilder : never : TE extends keyof DB - ? LeftJoinedBuilder - : TE extends AliasedExpression - ? LeftJoinedBuilder - : TE extends (qb: any) => AliasedExpression - ? LeftJoinedBuilder - : never + ? LeftJoinedBuilder + : TE extends AliasedExpression + ? LeftJoinedBuilder + : TE extends (qb: any) => AliasedExpression + ? LeftJoinedBuilder + : never type LeftJoinedBuilder< DB, @@ -1156,8 +1166,8 @@ type LeftJoinedDB = DrainOuterGeneric<{ [C in keyof DB | A]: C extends A ? Nullable : C extends keyof DB - ? DB[C] - : never + ? DB[C] + : never }> export type UpdateQueryBuilderWithRightJoin< @@ -1171,12 +1181,12 @@ export type UpdateQueryBuilderWithRightJoin< ? RightJoinedBuilder : never : TE extends keyof DB - ? RightJoinedBuilder - : TE extends AliasedExpression - ? RightJoinedBuilder - : TE extends (qb: any) => AliasedExpression - ? RightJoinedBuilder - : never + ? RightJoinedBuilder + : TE extends AliasedExpression + ? RightJoinedBuilder + : TE extends (qb: any) => AliasedExpression + ? RightJoinedBuilder + : never type RightJoinedBuilder< DB, @@ -1196,10 +1206,10 @@ type RightJoinedDB< [C in keyof DB | A]: C extends A ? R : C extends TB - ? Nullable - : C extends keyof DB - ? DB[C] - : never + ? Nullable + : C extends keyof DB + ? DB[C] + : never }> export type UpdateQueryBuilderWithFullJoin< @@ -1213,12 +1223,12 @@ export type UpdateQueryBuilderWithFullJoin< ? OuterJoinedBuilder : never : TE extends keyof DB - ? OuterJoinedBuilder - : TE extends AliasedExpression - ? OuterJoinedBuilder - : TE extends (qb: any) => AliasedExpression - ? OuterJoinedBuilder - : never + ? OuterJoinedBuilder + : TE extends AliasedExpression + ? OuterJoinedBuilder + : TE extends (qb: any) => AliasedExpression + ? OuterJoinedBuilder + : never type OuterJoinedBuilder< DB, @@ -1238,8 +1248,8 @@ type OuterJoinedBuilderDB< [C in keyof DB | A]: C extends A ? Nullable : C extends TB - ? Nullable - : C extends keyof DB - ? DB[C] - : never + ? Nullable + : C extends keyof DB + ? DB[C] + : never }> diff --git a/test/node/src/delete.test.ts b/test/node/src/delete.test.ts index efa0c61d4..b7024f73f 100644 --- a/test/node/src/delete.test.ts +++ b/test/node/src/delete.test.ts @@ -299,6 +299,35 @@ for (const dialect of DIALECTS) { await query.execute() }) + it('should delete from t1 using t2, t3 returning t2.*', async () => { + const query = ctx.db + .deleteFrom('toy') + .using(['pet', 'person']) + .whereRef('toy.pet_id', '=', 'pet.id') + .whereRef('pet.owner_id', '=', 'person.id') + .where('person.first_name', '=', 'Bob') + .returningAll('pet') + + testSql(query, dialect, { + postgres: { + sql: [ + 'delete from "toy"', + 'using "pet", "person"', + 'where "toy"."pet_id" = "pet"."id"', + 'and "pet"."owner_id" = "person"."id"', + 'and "person"."first_name" = $1', + 'returning "pet".*', + ], + parameters: ['Bob'], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await query.execute() + }) + it('should delete from t1 returning *', async () => { const query = ctx.db .deleteFrom('pet') diff --git a/test/node/src/object-util.test.ts b/test/node/src/object-util.test.ts new file mode 100644 index 000000000..fd70c1bdc --- /dev/null +++ b/test/node/src/object-util.test.ts @@ -0,0 +1,30 @@ +import { isPlainObject } from '../../../dist/cjs/util/object-utils' + +import { expect } from './test-setup.js' + +describe('object util', () => { + it('isPlainObject', async () => { + class SomeClass {} + + for (const obj of [{ foo: 'bar' }, Object.create(null)]) { + expect(isPlainObject(obj)).to.equal(true) + } + + for (const obj of [ + [], + new Date(), + Buffer.allocUnsafe(10), + new ArrayBuffer(10), + new Int32Array(10), + new Float64Array(10), + '', + 42, + false, + null, + undefined, + new SomeClass(), + ]) { + expect(isPlainObject(obj)).to.equal(false) + } + }) +}) diff --git a/test/node/src/update.test.ts b/test/node/src/update.test.ts index 7a5b69c18..5e4639cd2 100644 --- a/test/node/src/update.test.ts +++ b/test/node/src/update.test.ts @@ -478,6 +478,34 @@ for (const dialect of DIALECTS) { }) } + if (dialect === 'postgres') { + it('should update some rows and return joined rows when `returningAll` is used', async () => { + const query = ctx.db + .updateTable('person') + .from('pet') + .set({ + first_name: (eb) => eb.ref('pet.name'), + }) + .whereRef('pet.owner_id', '=', 'person.id') + .where('person.first_name', '=', 'Arnold') + .returningAll('pet') + + testSql(query, dialect, { + postgres: { + sql: 'update "person" set "first_name" = "pet"."name" from "pet" where "pet"."owner_id" = "person"."id" and "person"."first_name" = $1 returning "pet".*', + parameters: ['Arnold'], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + const result = await query.execute() + expect(result[0].name).to.equal('Doggo') + expect(result[0].species).to.equal('dog') + }) + } + if (dialect === 'postgres') { it('should update multiple rows and stream returned results', async () => { const stream = ctx.db diff --git a/test/typings/test-d/delete-query-builder.test-d.ts b/test/typings/test-d/delete-query-builder.test-d.ts index f827d0a3b..fbf9a0590 100644 --- a/test/typings/test-d/delete-query-builder.test-d.ts +++ b/test/typings/test-d/delete-query-builder.test-d.ts @@ -52,6 +52,15 @@ async function testDelete(db: Kysely) { .executeTakeFirstOrThrow() expectType(r7) + const r8 = await db + .deleteFrom('pet') + .using('person') + .leftJoin('pet', 'pet.owner_id', 'person.id') + .where('person.id', '=', 1) + .returningAll('person') + .executeTakeFirstOrThrow() + expectType>(r8) + expectError(db.deleteFrom('NO_SUCH_TABLE')) expectError(db.deleteFrom('pet').where('NO_SUCH_COLUMN', '=', '1')) expectError(db.deleteFrom('pet').whereRef('owner_id', '=', 'NO_SUCH_COLUMN')) diff --git a/test/typings/test-d/infer-result.test-d.ts b/test/typings/test-d/infer-result.test-d.ts index e48cbac61..088a191b9 100644 --- a/test/typings/test-d/infer-result.test-d.ts +++ b/test/typings/test-d/infer-result.test-d.ts @@ -80,9 +80,23 @@ function testInferResultUpdateQuery(db: Kysely) { const query1 = query0.returningAll() const compiledQuery1 = query1.compile() - type Expected1 = Selectable[] - expectType>>(true) - expectType>>(true) + type Expected1 = { + id: string | number + first_name: string + last_name: string | null + age: number + gender: 'male' | 'female' | 'other' + marital_status: 'single' | 'married' | 'divorced' | 'widowed' | null + modified_at: Date + deleted_at: Date | null + name: string + owner_id: number + species: 'dog' | 'cat' + }[] + + const expected1: Expected1 = undefined! + expectType>(expected1) + expectType>(expected1) const query2 = query0.returning('modified_at') const compiledQuery2 = query2.compile() diff --git a/test/typings/test-d/update.test-d.ts b/test/typings/test-d/update.test-d.ts index e4b1ba883..3721de1eb 100644 --- a/test/typings/test-d/update.test-d.ts +++ b/test/typings/test-d/update.test-d.ts @@ -31,6 +31,22 @@ async function testUpdate(db: Kysely) { .executeTakeFirst() expectType(r4) + const r5 = await db + .updateTable('pet') + .from((eb) => + eb + .selectFrom('person') + .select(['person.id as person_id', 'first_name as fn']) + .where('person.first_name', '=', 'Jennifer') + .forUpdate() + .skipLocked() + .as('p'), + ) + .set('name', (eb) => eb.ref('p.fn')) + .returningAll('p') + .execute() + expectType<{ fn: string; person_id: number }[]>(r5) + // Non-existent column expectError( db