Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
da0d4d7
fix: allow undefined values in Updateable type (#1496)
y-hsgw Jun 29, 2025
94f56e6
feat(Introspect): add support for postgres & mysql foreign tables (#1…
williamluke4 Jun 29, 2025
4b546b6
feat: add PGlite dialect. (#1510)
igalklebanov Jul 20, 2025
361e616
feat(migrator): allow disabling transactions in migrate methods. (#1517)
igalklebanov Jul 23, 2025
16fea09
feat: allow expressions in unique constraint (#1518)
ericsodev Jul 26, 2025
90d6c71
update lockfile.
igalklebanov Jul 29, 2025
01a1e22
chore: fix `next` branch rebase issues. (#1584)
igalklebanov Sep 20, 2025
214c6ae
feat: add `thenRef` method in `eb.case` (#1531)
ericsodev Sep 20, 2025
76e5d61
Support dropping multiple types with schema.dropType(), cascade. (#1516)
aantia Sep 27, 2025
da44fbe
fix: allow undefined values in Updateable type (#1496)
y-hsgw Jun 29, 2025
56bb67e
feat(Introspect): add support for postgres & mysql foreign tables (#1…
williamluke4 Jun 29, 2025
d56c64d
feat: add PGlite dialect. (#1510)
igalklebanov Jul 20, 2025
86427c1
feat(migrator): allow disabling transactions in migrate methods. (#1517)
igalklebanov Jul 23, 2025
ac8b0bd
feat: allow expressions in unique constraint (#1518)
ericsodev Jul 26, 2025
5bb01c8
update lockfile.
igalklebanov Jul 29, 2025
e60d4e1
chore: fix `next` branch rebase issues. (#1584)
igalklebanov Sep 20, 2025
a43ca4a
feat: add `thenRef` method in `eb.case` (#1531)
ericsodev Sep 20, 2025
9adaf87
Support dropping multiple types with schema.dropType(), cascade. (#1516)
aantia Sep 27, 2025
5e2153e
implemented `whenRef(lhs, op, rhs)` in `eb.case`. (#1598)
iam-abdul Oct 2, 2025
237179d
Merge branch 'next' of https://github.com/kysely-org/kysely into next
iam-abdul Oct 3, 2025
0dbcbca
added `elseRef` in `eb.case()` (#1601)
iam-abdul Oct 4, 2025
2962a91
Merge branch 'next' of https://github.com/kysely-org/kysely into next
iam-abdul Oct 4, 2025
73ad9ad
added setRef
iam-abdul Oct 6, 2025
8004afe
reverted lock
iam-abdul Oct 6, 2025
d33e829
implemented setRef #1413
iam-abdul Oct 12, 2025
09c6a27
Merge branch 'setRef_1413' of github.com:iam-abdul/kysely into setRef…
iam-abdul Oct 12, 2025
5417ee0
Merge branch 'next' of https://github.com/kysely-org/kysely into setR…
iam-abdul Oct 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/parser/update-set-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ export type UpdateObjectExpression<
UT extends keyof DB = TB,
> = UpdateObject<DB, TB, UT> | UpdateObjectFactory<DB, TB, UT>

export type UpdateObjectWithRef<
DB,
TB extends keyof DB,
UT extends keyof DB = TB,
> = DrainOuterGeneric<{
[C in AnyColumn<DB, UT>]?: ReferenceExpression<DB, TB>
}>

export type ExtractUpdateTypeFromReferenceExpression<
DB,
TB extends keyof DB,
Expand All @@ -62,6 +70,36 @@ export function parseUpdate(
return parseUpdateObjectExpression(args[0])
}

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

return parseUpdateObjectWithRef(args[0])
}

export function parseUpdateObjectWithRef(
update: UpdateObjectWithRef<any, any, any>,
): ReadonlyArray<ColumnUpdateNode> {
return Object.entries(update)
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => {
return ColumnUpdateNode.create(
ColumnNode.create(key),
parseReferenceExpression(value!),
)
})
}

export function parseUpdateObjectExpression(
update: UpdateObjectExpression<any, any, any>,
): ReadonlyArray<ColumnUpdateNode> {
Expand Down
77 changes: 77 additions & 0 deletions src/query-builder/update-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ import {
import { UpdateQueryNode } from '../operation-node/update-query-node.js'
import {
UpdateObjectExpression,
UpdateObjectWithRef,
ExtractUpdateTypeFromReferenceExpression,
parseUpdate,
parseUpdateWithRef,
} from '../parser/update-set-parser.js'
import { Compilable } from '../util/compilable.js'
import { QueryExecutor } from '../query-executor/query-executor.js'
Expand Down Expand Up @@ -743,6 +745,81 @@ export class UpdateQueryBuilder<DB, UT extends keyof DB, TB extends keyof DB, O>
})
}

/**
* Sets the values to update for an {@link Kysely.updateTable | update} query
* using column references instead of values.
*
* This method is similar to {@link set} but allows you to update columns by
* referencing other columns instead of providing literal values. This is useful
* when you want to copy values from one column to another or perform updates
* based on existing column values.
*
* You can provide either two arguments (column name and reference) or a single
* object where keys are column names and values are column references.
*
* ### Examples
*
* Update a column by referencing another column using the two-argument form:
*
* ```ts
* const result = await db
* .updateTable('person')
* .setRef('last_name', 'first_name')
* .where('id', '=', 1)
* .executeTakeFirst()
* ```
*
* The generated SQL (PostgreSQL):
*
* ```sql
* update "person" set "last_name" = "first_name" where "id" = $1
* ```
*
* You can reference columns from joined tables in a PostgreSQL `from` query:
*
* ```ts
* const result = await db
* .updateTable('person')
* .from('pet')
* .setRef({
* first_name: 'pet.name',
* })
* .whereRef('pet.owner_id', '=', 'person.id')
* .executeTakeFirst()
* ```
*
* The generated SQL (PostgreSQL):
*
* ```sql
* update "person"
* set "first_name" = "pet"."name"
* from "pet"
* where "pet"."owner_id" = "person"."id"
* ```
*/
setRef<RE extends ReferenceExpression<DB, UT>>(
key: RE,
value: RE,
): UpdateQueryBuilder<DB, UT, TB, O>

setRef(
updates: UpdateObjectWithRef<DB, TB, UT>,
): UpdateQueryBuilder<DB, UT, TB, O>

setRef(
...args:
| [ReferenceExpression<DB, UT>, ReferenceExpression<DB, UT>]
| [UpdateObjectWithRef<DB, TB, UT>]
): UpdateQueryBuilder<DB, UT, TB, O> {
return new UpdateQueryBuilder({
...this.#props,
queryNode: UpdateQueryNode.cloneWithUpdates(
this.#props.queryNode,
parseUpdateWithRef(...args),
),
})
}

returning<SE extends SelectExpression<DB, TB>>(
selections: ReadonlyArray<SE>,
): UpdateQueryBuilder<DB, UT, TB, ReturningRow<DB, TB, O, SE>>
Expand Down
90 changes: 90 additions & 0 deletions test/node/src/update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,96 @@ for (const dialect of DIALECTS) {
expect(jennifer.last_name).to.equal('Jennifer')
})

it('should update one row using setRef', async () => {
const query = ctx.db
.updateTable('person')
.setRef('last_name', 'first_name')
.where('first_name', '=', 'Jennifer')

testSql(query, dialect, {
postgres: {
sql: 'update "person" set "last_name" = "first_name" where "first_name" = $1',
parameters: ['Jennifer'],
},
mysql: {
sql: 'update `person` set `last_name` = `first_name` where `first_name` = ?',
parameters: ['Jennifer'],
},
mssql: {
sql: 'update "person" set "last_name" = "first_name" where "first_name" = @1',
parameters: ['Jennifer'],
},
sqlite: {
sql: 'update "person" set "last_name" = "first_name" where "first_name" = ?',
parameters: ['Jennifer'],
},
})

const result = await query.executeTakeFirst()

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

const jennifer = await ctx.db
.selectFrom('person')
.where('first_name', '=', 'Jennifer')
.select('last_name')
.executeTakeFirstOrThrow()

expect(jennifer.last_name).to.equal('Jennifer')
})

it('should update one row using setRef with object', async () => {
const query = ctx.db
.updateTable('person')
.setRef({
last_name: 'first_name',
})
.where('first_name', '=', 'Jennifer')

testSql(query, dialect, {
postgres: {
sql: 'update "person" set "last_name" = "first_name" where "first_name" = $1',
parameters: ['Jennifer'],
},
mysql: {
sql: 'update `person` set `last_name` = `first_name` where `first_name` = ?',
parameters: ['Jennifer'],
},
mssql: {
sql: 'update "person" set "last_name" = "first_name" where "first_name" = @1',
parameters: ['Jennifer'],
},
sqlite: {
sql: 'update "person" set "last_name" = "first_name" where "first_name" = ?',
parameters: ['Jennifer'],
},
})

const result = await query.executeTakeFirst()

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

const jennifer = await ctx.db
.selectFrom('person')
.where('first_name', '=', 'Jennifer')
.select('last_name')
.executeTakeFirstOrThrow()

expect(jennifer.last_name).to.equal('Jennifer')
})

it('should update one row while ignoring undefined values', async () => {
const query = ctx.db
.updateTable('person')
Expand Down
28 changes: 28 additions & 0 deletions test/typings/test-d/update.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ async function testUpdate(db: Kysely<Database>) {
.execute()
expectType<{ fn: string; person_id: number }[]>(r5)

const r6 = await db
.updateTable('pet as p')
.where('p.id', '=', '1')
.setRef('name', 'species')
.executeTakeFirst()
expectType<UpdateResult>(r6)

// Non-existent column
expectError(
db
Expand All @@ -55,6 +62,27 @@ async function testUpdate(db: Kysely<Database>) {
.set({ name: 'Fluffy', not_a_column: 'not_a_column' }),
)

expectError(
db
.updateTable('pet as p')
.where('p.id', '=', '1')
.setRef('name', 'not_a_column'),
)

expectError(
db
.updateTable('pet as p')
.where('p.id', '=', '1')
.setRef({ name: 'not_a_column' }),
)

expectError(
db
.updateTable('pet as p')
.where('p.id', '=', '1')
.setRef({ not_a_column: 'not_a_column' }),
)

// Non-existent column in a callback
expectError(
db
Expand Down