Skip to content

Commit

Permalink
feat: add deleteQueryBuilder.returningAll(table) overload (#268) (#314
Browse files Browse the repository at this point in the history
)

* feat: add `deleteQueryBuilder.returningAll(table)` overload (#268)

* docs: for `deleteQueryBuilder.returningAll()` overload

* chore: fix typos
  • Loading branch information
anirudh1713 authored Mar 6, 2023
1 parent a8e28d9 commit 5b48fc8
Show file tree
Hide file tree
Showing 5 changed files with 395 additions and 8 deletions.
10 changes: 9 additions & 1 deletion src/parser/returning-parser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DeleteResult } from '../query-builder/delete-result.js'
import { InsertResult } from '../query-builder/insert-result.js'
import { UpdateResult } from '../query-builder/update-result.js'
import { Selection } from './select-parser.js'
import { Selection, AllSelection } from './select-parser.js'

export type ReturningRow<
DB,
Expand All @@ -15,3 +15,11 @@ export type ReturningRow<
: O extends UpdateResult
? Selection<DB, TB, SE>
: O & Selection<DB, TB, SE>

export type ReturningAllRow<DB, TB extends keyof DB, O> = O extends InsertResult
? AllSelection<DB, TB>
: O extends DeleteResult
? AllSelection<DB, TB>
: O extends UpdateResult
? AllSelection<DB, TB>
: O & AllSelection<DB, TB>
2 changes: 1 addition & 1 deletion src/parser/select-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ type ExtractTypeFromStringSelectExpression<
: never
: never

type AllSelection<DB, TB extends keyof DB> = Selectable<{
export type AllSelection<DB, TB extends keyof DB> = Selectable<{
[C in AnyColumn<DB, TB>]: {
[T in TB]: C extends keyof DB[T] ? DB[T][C] : never
}[TB]
Expand Down
109 changes: 105 additions & 4 deletions src/query-builder/delete-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
SelectExpression,
SelectExpressionOrList,
} from '../parser/select-parser.js'
import { ReturningRow } from '../parser/returning-parser.js'
import { ReturningAllRow, ReturningRow } from '../parser/returning-parser.js'
import { ReferenceExpression } from '../parser/reference-parser.js'
import { QueryNode } from '../operation-node/query-node.js'
import {
Expand All @@ -38,7 +38,6 @@ import { ReturningInterface } from './returning-interface.js'
import { NoResultError, NoResultErrorConstructor } from './no-result-error.js'
import { DeleteResult } from './delete-result.js'
import { DeleteQueryNode } from '../operation-node/delete-query-node.js'
import { Selectable } from '../util/column-type.js'
import { LimitNode } from '../operation-node/limit-node.js'
import {
OrderByDirectionExpression,
Expand Down Expand Up @@ -503,12 +502,114 @@ export class DeleteQueryBuilder<DB, TB extends keyof DB, O>
})
}

returningAll(): DeleteQueryBuilder<DB, TB, Selectable<DB[TB]>> {
/**
* Adds `returning *` or `returning table.*` clause to the query.
*
* ### Examples
*
* Return all columns.
*
* ```ts
* const pets = await db
* .deleteFrom('pet')
* .returningAll()
* .execute()
* ```
*
* The generated SQL (PostgreSQL)
*
* ```sql
* delete from "pet" returning *
* ```
*
* Return all columns from all tables
*
* ```ts
* const result = ctx.db
* .deleteFrom('toy')
* .using(['pet', 'person'])
* .whereRef('toy.pet_id', '=', 'pet.id')
* .whereRef('pet.owner_id', '=', 'person.id')
* .where('person.first_name', '=', 'Zoro')
* .returningAll()
* .execute()
* ```
*
* The generated SQL (PostgreSQL)
*
* ```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 *
* ```
*
* Return all columns from a single table.
*
* ```ts
* const result = ctx.db
* .deleteFrom('toy')
* .using(['pet', 'person'])
* .whereRef('toy.pet_id', '=', 'pet.id')
* .whereRef('pet.owner_id', '=', 'person.id')
* .where('person.first_name', '=', 'Itachi')
* .returningAll('pet')
* .execute()
* ```
*
* The generated SQL (PostgreSQL)
*
* ```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".*
* ```
*
* Return all columns from multiple tables.
*
* ```ts
* const result = ctx.db
* .deleteFrom('toy')
* .using(['pet', 'person'])
* .whereRef('toy.pet_id', '=', 'pet.id')
* .whereRef('pet.owner_id', '=', 'person.id')
* .where('person.first_name', '=', 'Luffy')
* .returningAll(['toy', 'pet'])
* .execute()
* ```
*
* The generated SQL (PostgreSQL)
*
* ```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 "toy".*, "pet".*
* ```
*/
returningAll<T extends TB>(
tables: ReadonlyArray<T>
): DeleteQueryBuilder<DB, TB, ReturningAllRow<DB, T, O>>

returningAll<T extends TB>(
table: T
): DeleteQueryBuilder<DB, TB, ReturningAllRow<DB, T, O>>

returningAll(): DeleteQueryBuilder<DB, TB, ReturningAllRow<DB, TB, O>>

returningAll(table?: any): any {
return new DeleteQueryBuilder({
...this.#props,
queryNode: QueryNode.cloneWithReturning(
this.#props.queryNode,
parseSelectAll()
parseSelectAll(table)
),
})
}
Expand Down
174 changes: 174 additions & 0 deletions test/node/src/delete.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,180 @@ for (const dialect of BUILT_IN_DIALECTS) {

await query.execute()
})

it('should delete from t1 returning *', async () => {
const query = ctx.db
.deleteFrom('pet')
.where('pet.species', '=', 'cat')
.returningAll()

testSql(query, dialect, {
postgres: {
sql: [
'delete from "pet"',
'where "pet"."species" = $1',
'returning *',
],
parameters: ['cat'],
},
mysql: NOT_SUPPORTED,
sqlite: NOT_SUPPORTED,
})

await query.execute()
})

it('should delete from t1 using t2, t3 returning *', 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', '=', 'Zoro')
.returningAll()

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 *',
],
parameters: ['Zoro'],
},
mysql: NOT_SUPPORTED,
sqlite: NOT_SUPPORTED,
})

await query.execute()
})

it('should delete from t1 using t2, t3 returning t1.*, 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', '=', 'Luffy')
.returningAll(['toy', '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 "toy".*, "pet".*',
],
parameters: ['Luffy'],
},
mysql: NOT_SUPPORTED,
sqlite: NOT_SUPPORTED,
})

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', '=', 'Itachi')
.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: ['Itachi'],
},
mysql: NOT_SUPPORTED,
sqlite: NOT_SUPPORTED,
})

await query.execute()
})

it('should delete from t1 returning t1.*', async () => {
const query = ctx.db
.deleteFrom('person')
.where('gender', '=', 'male')
.returningAll('person')

testSql(query, dialect, {
postgres: {
sql: [
'delete from "person"',
'where "gender" = $1',
'returning "person".*',
],
parameters: ['male'],
},
mysql: NOT_SUPPORTED,
sqlite: NOT_SUPPORTED,
})

await query.execute()
})

it('should delete from t1 returning *', async () => {
const query = ctx.db
.deleteFrom('person')
.where('gender', '=', 'male')
.returningAll()

testSql(query, dialect, {
postgres: {
sql: ['delete from "person"', 'where "gender" = $1', 'returning *'],
parameters: ['male'],
},
mysql: NOT_SUPPORTED,
sqlite: NOT_SUPPORTED,
})

await query.execute()
})

it('should delete from t1 using t2, t3 returning *', 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()

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 *',
],
parameters: ['Bob'],
},
mysql: NOT_SUPPORTED,
sqlite: NOT_SUPPORTED,
})

await query.execute()
})
}

if (dialect === 'mysql') {
Expand Down
Loading

0 comments on commit 5b48fc8

Please sign in to comment.