Skip to content

Commit

Permalink
add set(key, value) variant of update set function. Closes #672
Browse files Browse the repository at this point in the history
  • Loading branch information
koskimas committed Sep 2, 2023
1 parent 848f4db commit 33dfa4c
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 21 deletions.
5 changes: 2 additions & 3 deletions src/operation-node/column-update-node.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { freeze } from '../util/object-utils.js'
import { ColumnNode } from './column-node.js'
import { OperationNode } from './operation-node.js'

export interface ColumnUpdateNode extends OperationNode {
readonly kind: 'ColumnUpdateNode'
readonly column: ColumnNode
readonly column: OperationNode
readonly value: OperationNode
}

Expand All @@ -16,7 +15,7 @@ export const ColumnUpdateNode = freeze({
return node.kind === 'ColumnUpdateNode'
},

create(column: ColumnNode, value: OperationNode): ColumnUpdateNode {
create(column: OperationNode, value: OperationNode): ColumnUpdateNode {
return freeze({
kind: 'ColumnUpdateNode',
column,
Expand Down
11 changes: 9 additions & 2 deletions src/parser/reference-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
isExpressionOrFactory,
} from './expression-parser.js'
import { DynamicReferenceBuilder } from '../dynamic/dynamic-reference-builder.js'
import { SelectType } from '../util/column-type.js'
import { SelectType, UpdateType } from '../util/column-type.js'
import { IdentifierNode } from '../operation-node/identifier-node.js'
import { OperationNode } from '../operation-node/operation-node.js'
import { Expression } from '../expression/expression.js'
Expand Down Expand Up @@ -55,8 +55,15 @@ export type ExtractTypeFromReferenceExpression<
TB extends keyof DB,
RE,
DV = unknown
> = SelectType<ExtractRawTypeFromReferenceExpression<DB, TB, RE, DV>>

export type ExtractRawTypeFromReferenceExpression<
DB,
TB extends keyof DB,
RE,
DV = unknown
> = RE extends string
? SelectType<ExtractTypeFromStringReference<DB, TB, RE>>
? ExtractTypeFromStringReference<DB, TB, RE>
: RE extends SelectQueryBuilderExpression<infer O>
? O[keyof O] | null
: RE extends (qb: any) => SelectQueryBuilderExpression<infer O>
Expand Down
35 changes: 32 additions & 3 deletions src/parser/update-set-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import {
import { UpdateKeys, UpdateType } from '../util/column-type.js'
import { isFunction } from '../util/object-utils.js'
import { parseValueExpression, ValueExpression } from './value-parser.js'
import {
ExtractRawTypeFromReferenceExpression,
parseReferenceExpression,
ReferenceExpression,
} from './reference-parser.js'

export type UpdateObject<DB, TB extends keyof DB, UT extends keyof DB = TB> = {
[C in UpdateKeys<DB[UT]>]?:
Expand All @@ -20,14 +25,38 @@ export type UpdateObjectFactory<
UT extends keyof DB
> = (eb: ExpressionBuilder<DB, TB>) => UpdateObject<DB, TB, UT>

export type UpdateExpression<
export type UpdateObjectExpression<
DB,
TB extends keyof DB,
UT extends keyof DB = TB
> = UpdateObject<DB, TB, UT> | UpdateObjectFactory<DB, TB, UT>

export function parseUpdateExpression(
update: UpdateExpression<any, any, any>
export type ExtractUpdateTypeFromReferenceExpression<
DB,
TB extends keyof DB,
RE,
DV = unknown
> = UpdateType<ExtractRawTypeFromReferenceExpression<DB, TB, RE, DV>>

export function parseUpdate(
...args:
| [UpdateObjectExpression<any, any, any>]
| [ReferenceExpression<any, any>, ValueExpression<any, any, any>]
): ReadonlyArray<ColumnUpdateNode> {
if (args.length === 2) {
return [
ColumnUpdateNode.create(
parseReferenceExpression(args[0]),
parseValueExpression(args[1])
),
]
}

return parseUpdateObjectExpression(args[0])
}

export function parseUpdateObjectExpression(
update: UpdateObjectExpression<any, any, any>
): ReadonlyArray<ColumnUpdateNode> {
const updateObj = isFunction(update) ? update(expressionBuilder()) : update

Expand Down
8 changes: 4 additions & 4 deletions src/query-builder/insert-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import {
SimplifySingleResult,
} from '../util/type-utils.js'
import {
UpdateExpression,
parseUpdateExpression,
UpdateObjectExpression,
parseUpdateObjectExpression,
} from '../parser/update-set-parser.js'
import { preventAwait } from '../util/prevent-await.js'
import { Compilable } from '../util/compilable.js'
Expand Down Expand Up @@ -531,13 +531,13 @@ export class InsertQueryBuilder<DB, TB extends keyof DB, O>
* ```
*/
onDuplicateKeyUpdate(
update: UpdateExpression<DB, TB, TB>
update: UpdateObjectExpression<DB, TB, TB>
): InsertQueryBuilder<DB, TB, O> {
return new InsertQueryBuilder({
...this.#props,
queryNode: InsertQueryNode.cloneWith(this.#props.queryNode, {
onDuplicateKey: OnDuplicateKeyNode.create(
parseUpdateExpression(update)
parseUpdateObjectExpression(update)
),
}),
})
Expand Down
8 changes: 4 additions & 4 deletions src/query-builder/on-conflict-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
import { ExpressionOrFactory } from '../parser/expression-parser.js'
import { ReferenceExpression } from '../parser/reference-parser.js'
import {
UpdateExpression,
parseUpdateExpression,
UpdateObjectExpression,
parseUpdateObjectExpression,
} from '../parser/update-set-parser.js'
import { freeze } from '../util/object-utils.js'
import { preventAwait } from '../util/prevent-await.js'
Expand Down Expand Up @@ -227,7 +227,7 @@ export class OnConflictBuilder<DB, TB extends keyof DB>
* ```
*/
doUpdateSet(
update: UpdateExpression<
update: UpdateObjectExpression<
OnConflictDatabase<DB, TB>,
OnConflictTables<TB>,
OnConflictTables<TB>
Expand All @@ -236,7 +236,7 @@ export class OnConflictBuilder<DB, TB extends keyof DB>
return new OnConflictUpdateBuilder({
...this.#props,
onConflictNode: OnConflictNode.cloneWith(this.#props.onConflictNode, {
updates: parseUpdateExpression(update),
updates: parseUpdateObjectExpression(update),
}),
})
}
Expand Down
29 changes: 24 additions & 5 deletions src/query-builder/update-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,17 @@ import {
} from '../util/type-utils.js'
import { UpdateQueryNode } from '../operation-node/update-query-node.js'
import {
parseUpdateExpression,
UpdateExpression,
UpdateObjectExpression,
UpdateObject,
UpdateObjectFactory,
ExtractUpdateTypeFromReferenceExpression,
parseUpdate,
} from '../parser/update-set-parser.js'
import { preventAwait } from '../util/prevent-await.js'
import { Compilable } from '../util/compilable.js'
import { QueryExecutor } from '../query-executor/query-executor.js'
import { QueryId } from '../util/query-id.js'
import { freeze } from '../util/object-utils.js'
import { freeze, isObject } from '../util/object-utils.js'
import { UpdateResult } from './update-result.js'
import { KyselyPlugin } from '../plugin/kysely-plugin.js'
import { WhereInterface } from './where-interface.js'
Expand All @@ -67,6 +68,11 @@ import {
import { KyselyTypeError } from '../util/type-error.js'
import { Streamable } from '../util/streamable.js'
import { ExpressionOrFactory } from '../parser/expression-parser.js'
import {
ValueExpression,
parseValueExpression,
} from '../parser/value-parser.js'
import { ColumnUpdateNode } from '../operation-node/column-update-node.js'

export class UpdateQueryBuilder<DB, UT extends keyof DB, TB extends keyof DB, O>
implements
Expand Down Expand Up @@ -519,12 +525,25 @@ export class UpdateQueryBuilder<DB, UT extends keyof DB, TB extends keyof DB, O>
update: UpdateObjectFactory<DB, TB, UT>
): UpdateQueryBuilder<DB, UT, TB, O>

set(update: UpdateExpression<DB, TB, UT>): UpdateQueryBuilder<DB, UT, TB, O> {
set<RE extends ReferenceExpression<DB, UT>>(
key: RE,
value: ValueExpression<
DB,
TB,
ExtractUpdateTypeFromReferenceExpression<DB, UT, RE>
>
): UpdateQueryBuilder<DB, UT, TB, O>

set(
...args:
| [UpdateObjectExpression<DB, TB, UT>]
| [ReferenceExpression<DB, UT>, ValueExpression<DB, UT, unknown>]
): UpdateQueryBuilder<DB, UT, TB, O> {
return new UpdateQueryBuilder({
...this.#props,
queryNode: UpdateQueryNode.cloneWithUpdates(
this.#props.queryNode,
parseUpdateExpression(update)
parseUpdate(...args)
),
})
}
Expand Down
46 changes: 46 additions & 0 deletions test/node/src/update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,52 @@ for (const dialect of DIALECTS) {
])
})

it('should update one row using the (key, value) variant of `set` method', async () => {
const query = ctx.db
.updateTable('person')
.set('first_name', 'Foo')
.set((eb) => eb.ref('last_name'), 'Barson')
.where('gender', '=', 'female')

testSql(query, dialect, {
postgres: {
sql: 'update "person" set "first_name" = $1, "last_name" = $2 where "gender" = $3',
parameters: ['Foo', 'Barson', 'female'],
},
mysql: {
sql: 'update `person` set `first_name` = ?, `last_name` = ? where `gender` = ?',
parameters: ['Foo', 'Barson', 'female'],
},
sqlite: {
sql: 'update "person" set "first_name" = ?, "last_name" = ? where "gender" = ?',
parameters: ['Foo', 'Barson', 'female'],
},
})

const result = await query.executeTakeFirst()

expect(result).to.be.instanceOf(UpdateResult)
expect(result.numUpdatedRows).to.equal(1n)
if (dialect === 'mysql') {
expect(result.numChangedRows).to.equal(1n)
} else {
expect(result.numChangedRows).to.undefined
}

expect(
await ctx.db
.selectFrom('person')
.select(['first_name', 'last_name', 'gender'])
.orderBy('first_name')
.orderBy('last_name')
.execute()
).to.eql([
{ first_name: 'Arnold', last_name: 'Schwarzenegger', gender: 'male' },
{ first_name: 'Foo', last_name: 'Barson', gender: 'female' },
{ first_name: 'Sylvester', last_name: 'Stallone', gender: 'male' },
])
})

it('should update one row with table alias', async () => {
const query = ctx.db
.updateTable('person as p')
Expand Down

1 comment on commit 33dfa4c

@vercel
Copy link

@vercel vercel bot commented on 33dfa4c Sep 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

kysely – ./

www.kysely.dev
kysely-git-master-kysely-team.vercel.app
kysely-kysely-team.vercel.app
kysely.dev

Please sign in to comment.