Skip to content

Commit

Permalink
feat(pg): implement QueryBuilder#updateFrom (#5386)
Browse files Browse the repository at this point in the history
  • Loading branch information
wms committed Jul 6, 2023
1 parent 34f85ba commit 61ee533
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 1 deletion.
5 changes: 5 additions & 0 deletions lib/dialects/postgres/query/pg-querybuilder.js
@@ -1,6 +1,11 @@
const QueryBuilder = require('../../../query/querybuilder.js');

module.exports = class QueryBuilder_PostgreSQL extends QueryBuilder {
updateFrom(name) {
this._single.updateFrom = name;
return this;
}

using(tables) {
this._single.using = tables;
return this;
Expand Down
7 changes: 6 additions & 1 deletion lib/dialects/postgres/query/pg-querycompiler.js
Expand Up @@ -50,12 +50,13 @@ class QueryCompiler_PG extends QueryCompiler {
const withSQL = this.with();
const updateData = this._prepUpdate(this.single.update);
const wheres = this.where();
const { returning } = this.single;
const { returning, updateFrom } = this.single;
return {
sql:
withSQL +
`update ${this.single.only ? 'only ' : ''}${this.tableName} ` +
`set ${updateData.join(', ')}` +
this._updateFrom(updateFrom) +
(wheres ? ` ${wheres}` : '') +
this._returning(returning),
returning,
Expand Down Expand Up @@ -141,6 +142,10 @@ class QueryCompiler_PG extends QueryCompiler {
return value ? ` returning ${this.formatter.columnize(value)}` : '';
}

_updateFrom(name) {
return name ? ` from ${this.formatter.wrap(name)}` : '';
}

_ignore(columns) {
if (columns === true) {
return ' on conflict do nothing';
Expand Down
25 changes: 25 additions & 0 deletions test/integration2/query/update/updates.spec.js
Expand Up @@ -3,6 +3,7 @@
const { expect } = require('chai');

const { TEST_TIMESTAMP } = require('../../../util/constants');
const { isPostgreSQL } = require('../../../util/db-helpers');
const {
getAllDbs,
getKnexForDb,
Expand Down Expand Up @@ -534,6 +535,30 @@ describe('Updates', function () {
expect(results[0].last_name).to.equal('olivier');
});

it('should allow explicit from', async function () {
if (!isPostgreSQL(knex)) {
return this.skip();
}

await knex('accounts')
.update({ last_name: 'olivier' })
.with('withClause', function () {
this.select('id', 'last_name')
.from('accounts')
.where('email', '=', 'test1@example.com');
})
.updateFrom('withClause')
.where('withClause.id', '=', knex.ref('accounts.id'))
.testSql(function (tester) {
tester(
'pg',
'with "withClause" as (select "id", "last_name" from "accounts" where "email" = ?) update "accounts" set "last_name" = ? from "withClause" where "withClause"."id" = "accounts"."id"',
['test1@example.com', 'olivier'],
1
);
});
});

it('should escaped json objects when update value #5059', async function () {
await knex.schema.dropTableIfExists('testing');
await knex.schema.createTable('testing', (t) => {
Expand Down
16 changes: 16 additions & 0 deletions test/unit/query/builder.js
Expand Up @@ -6080,6 +6080,22 @@ describe('QueryBuilder', () => {
);
});

it('update from table', () => {
testsql(
qb()
.update({ email: 'foo', name: 'bar' })
.table('users')
.updateFrom('others')
.where('id', '=', 1),
{
pg: {
sql: 'update "users" set "email" = ?, "name" = ? from "others" where "id" = ?',
bindings: ['foo', 'bar', 1],
},
}
);
});

it('should not update columns undefined values', () => {
testsql(
qb()
Expand Down
2 changes: 2 additions & 0 deletions types/index.d.ts
Expand Up @@ -1151,6 +1151,8 @@ export declare namespace Knex {

onConflict(): OnConflictQueryBuilder<TRecord, TResult>;

updateFrom: Table<TRecord, TResult>;

del(
returning: '*',
options?: DMLOptions
Expand Down

0 comments on commit 61ee533

Please sign in to comment.