From 475e4fddf9af8981f69dd122bcd756a71c11cafb Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Fri, 23 Feb 2024 17:36:02 +0100 Subject: [PATCH 1/8] fix(`update`): smart quoting also for update statements this was missing, smart quotation is already in place for other statements --- db-service/lib/cqn2sql.js | 4 ++-- sqlite/test/general/keywords.test.js | 16 ++++++++++++++++ sqlite/test/general/testModel.cds | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 sqlite/test/general/keywords.test.js diff --git a/db-service/lib/cqn2sql.js b/db-service/lib/cqn2sql.js index 438c35e70..b1684552b 100644 --- a/db-service/lib/cqn2sql.js +++ b/db-service/lib/cqn2sql.js @@ -689,8 +689,8 @@ class CQN2SQLRenderer { UPDATE(q) { const { entity, with: _with, data, where } = q.UPDATE const elements = q.target?.elements - let sql = `UPDATE ${this.name(entity.ref?.[0] || entity)}` - if (entity.as) sql += ` AS ${entity.as}` + let sql = `UPDATE ${this.quote(this.name(entity.ref?.[0] || entity))}` + if (entity.as) sql += ` AS ${this.quote(entity.as)}` let columns = [] if (data) _add(data, val => this.val({ val })) diff --git a/sqlite/test/general/keywords.test.js b/sqlite/test/general/keywords.test.js new file mode 100644 index 000000000..073fabd1d --- /dev/null +++ b/sqlite/test/general/keywords.test.js @@ -0,0 +1,16 @@ +'use strict' +const cds = require('../../../test/cds.js') +cds.test(__dirname, 'testModel.cds') + +describe('keywords', () => { + test('insert, update, select', async () => { + // fill other table first + const { Order } = cds.entities + await INSERT({ ID: 42, order: 'foo' }).into(Order) + const select = await SELECT.from(Order).where(`Order.order = 'foo'`) + expect(select).toMatchObject([{ ID: 42, order: 'foo' }]) + await UPDATE.entity(Order).with({ order: 'bar' }).where({ ID: 42 }) + const selectAfterChange = await SELECT.from(Order).where(`Order.order = 'bar'`) + expect(selectAfterChange).toMatchObject([{ ID: 42, order: 'bar' }]) + }) +}) diff --git a/sqlite/test/general/testModel.cds b/sqlite/test/general/testModel.cds index e6badc3f5..c638d9f67 100644 --- a/sqlite/test/general/testModel.cds +++ b/sqlite/test/general/testModel.cds @@ -58,6 +58,11 @@ entity DBDeepEntityChild { otherName2 : String; } +entity Order { + key ID : Integer; + order: String; +} + entity EProjChild as projection on DBDeepEntityChild { ID as IDRename, parent as parentRename, From db672d17fc7ef9fb18db2660074a1c4116c461e3 Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Fri, 23 Feb 2024 18:10:07 +0100 Subject: [PATCH 2/8] move to compliance test suite --- sqlite/test/general/keywords.test.js | 16 --------- sqlite/test/general/testModel.cds | 5 --- test/compliance/keywords.test.js | 34 +++++++++++++++++++ test/compliance/resources/db/index.cds | 1 + .../resources/db/keywords/index.cds | 11 ++++++ 5 files changed, 46 insertions(+), 21 deletions(-) delete mode 100644 sqlite/test/general/keywords.test.js create mode 100644 test/compliance/keywords.test.js create mode 100644 test/compliance/resources/db/keywords/index.cds diff --git a/sqlite/test/general/keywords.test.js b/sqlite/test/general/keywords.test.js deleted file mode 100644 index 073fabd1d..000000000 --- a/sqlite/test/general/keywords.test.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' -const cds = require('../../../test/cds.js') -cds.test(__dirname, 'testModel.cds') - -describe('keywords', () => { - test('insert, update, select', async () => { - // fill other table first - const { Order } = cds.entities - await INSERT({ ID: 42, order: 'foo' }).into(Order) - const select = await SELECT.from(Order).where(`Order.order = 'foo'`) - expect(select).toMatchObject([{ ID: 42, order: 'foo' }]) - await UPDATE.entity(Order).with({ order: 'bar' }).where({ ID: 42 }) - const selectAfterChange = await SELECT.from(Order).where(`Order.order = 'bar'`) - expect(selectAfterChange).toMatchObject([{ ID: 42, order: 'bar' }]) - }) -}) diff --git a/sqlite/test/general/testModel.cds b/sqlite/test/general/testModel.cds index c638d9f67..e6badc3f5 100644 --- a/sqlite/test/general/testModel.cds +++ b/sqlite/test/general/testModel.cds @@ -58,11 +58,6 @@ entity DBDeepEntityChild { otherName2 : String; } -entity Order { - key ID : Integer; - order: String; -} - entity EProjChild as projection on DBDeepEntityChild { ID as IDRename, parent as parentRename, diff --git a/test/compliance/keywords.test.js b/test/compliance/keywords.test.js new file mode 100644 index 000000000..68cbf08ec --- /dev/null +++ b/test/compliance/keywords.test.js @@ -0,0 +1,34 @@ +'use strict' +const cds = require('../../test/cds.js') +const { expect } = cds.test(__dirname + '/resources') + +describe('keywords', () => { + test('insert, update, select', async () => { + // fill other table first + const { Order, Alter } = cds.entities + const data = { + ID: 1, + alter: [ + { + ID: 42, + number: null, + order_ID: 1, + }, + { + ID: 43, + number: null, + order_ID: 1, + }, + ], + } + await INSERT(data).into(Order) + const select = await cds.run(CQL`SELECT from Order { ID, alter { * } } where exists alter`) + expect(select[0]).to.deep.eql(data) + + data.alter.forEach(e => e.number = 99) + await UPDATE.entity(Order).with(data).where('exists alter') + + const selectAfterChange = await cds.run(CQL`SELECT from Order { ID, alter { * } } where exists alter`) + expect(selectAfterChange[0]).to.deep.eql(data) + }) +}) diff --git a/test/compliance/resources/db/index.cds b/test/compliance/resources/db/index.cds index 04630dff3..eb8873b50 100644 --- a/test/compliance/resources/db/index.cds +++ b/test/compliance/resources/db/index.cds @@ -3,3 +3,4 @@ using from './complex'; using from './edge'; using from './hana'; using from './timestamps'; +using from './keywords'; diff --git a/test/compliance/resources/db/keywords/index.cds b/test/compliance/resources/db/keywords/index.cds new file mode 100644 index 000000000..de5bccbaf --- /dev/null +++ b/test/compliance/resources/db/keywords/index.cds @@ -0,0 +1,11 @@ +// ORDER / ALTER is a reserved word in the ANSI SQL standard +entity Order { + key ID : Integer; + alter: composition of many Alter on alter.order = $self; +} + +entity Alter { + key ID : Integer; + number: Integer; + order: Association to Order; +} From 893b56d820cb8d94412b245d027670f738d7557e Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Fri, 23 Feb 2024 18:30:52 +0100 Subject: [PATCH 3/8] also escape in create statement --- db-service/lib/cqn2sql.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db-service/lib/cqn2sql.js b/db-service/lib/cqn2sql.js index b1684552b..2f985e4ab 100644 --- a/db-service/lib/cqn2sql.js +++ b/db-service/lib/cqn2sql.js @@ -113,8 +113,8 @@ class CQN2SQLRenderer { delete this.values this.sql = !query || target['@cds.persistence.table'] - ? `CREATE TABLE ${name} ( ${this.CREATE_elements(target.elements)} )` - : `CREATE VIEW ${name} AS ${this.SELECT(this.cqn4sql(query))}` + ? `CREATE TABLE ${this.quote(name)} ( ${this.CREATE_elements(target.elements)} )` + : `CREATE VIEW ${this.quote(name)} AS ${this.SELECT(this.cqn4sql(query))}` this.values = [] return } From 04dfbf628407c75a93109426cb07e579a0d79db6 Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Fri, 23 Feb 2024 22:18:54 +0100 Subject: [PATCH 4/8] linter --- test/compliance/keywords.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compliance/keywords.test.js b/test/compliance/keywords.test.js index 68cbf08ec..a6109372b 100644 --- a/test/compliance/keywords.test.js +++ b/test/compliance/keywords.test.js @@ -5,7 +5,7 @@ const { expect } = cds.test(__dirname + '/resources') describe('keywords', () => { test('insert, update, select', async () => { // fill other table first - const { Order, Alter } = cds.entities + const { Order } = cds.entities const data = { ID: 1, alter: [ From f2710914c00f25fdfc665a9749c02c1547901754 Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Fri, 23 Feb 2024 22:23:07 +0100 Subject: [PATCH 5/8] comments --- test/compliance/keywords.test.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/compliance/keywords.test.js b/test/compliance/keywords.test.js index a6109372b..15c2a3a55 100644 --- a/test/compliance/keywords.test.js +++ b/test/compliance/keywords.test.js @@ -4,7 +4,6 @@ const { expect } = cds.test(__dirname + '/resources') describe('keywords', () => { test('insert, update, select', async () => { - // fill other table first const { Order } = cds.entities const data = { ID: 1, @@ -25,8 +24,8 @@ describe('keywords', () => { const select = await cds.run(CQL`SELECT from Order { ID, alter { * } } where exists alter`) expect(select[0]).to.deep.eql(data) - data.alter.forEach(e => e.number = 99) - await UPDATE.entity(Order).with(data).where('exists alter') + data.alter.forEach(e => e.number = 99) // change data + await UPDATE.entity(Order).with(data).where('exists alter') const selectAfterChange = await cds.run(CQL`SELECT from Order { ID, alter { * } } where exists alter`) expect(selectAfterChange[0]).to.deep.eql(data) From 53f1013b45bd7ccddf9266c4fd786c1de8bddfe4 Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Fri, 23 Feb 2024 22:39:12 +0100 Subject: [PATCH 6/8] add test and fix for insert as select --- db-service/lib/cqn2sql.js | 2 +- test/compliance/keywords.test.js | 20 +++++++++++++++++-- .../resources/db/keywords/index.cds | 4 ++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/db-service/lib/cqn2sql.js b/db-service/lib/cqn2sql.js index 2f985e4ab..0ffea8316 100644 --- a/db-service/lib/cqn2sql.js +++ b/db-service/lib/cqn2sql.js @@ -617,7 +617,7 @@ class CQN2SQLRenderer { const columns = (this.columns = (INSERT.columns || ObjectKeys(elements)).filter( c => c in elements && !elements[c].virtual && !elements[c].isAssociation, )) - this.sql = `INSERT INTO ${entity}${alias ? ' as ' + this.quote(alias) : ''} (${columns}) ${this.SELECT( + this.sql = `INSERT INTO ${this.quote(entity)}${alias ? ' as ' + this.quote(alias) : ''} (${columns}) ${this.SELECT( this.cqn4sql(INSERT.as), )}` this.entries = [this.values] diff --git a/test/compliance/keywords.test.js b/test/compliance/keywords.test.js index 15c2a3a55..7aac512f2 100644 --- a/test/compliance/keywords.test.js +++ b/test/compliance/keywords.test.js @@ -24,10 +24,26 @@ describe('keywords', () => { const select = await cds.run(CQL`SELECT from Order { ID, alter { * } } where exists alter`) expect(select[0]).to.deep.eql(data) - data.alter.forEach(e => e.number = 99) // change data - await UPDATE.entity(Order).with(data).where('exists alter') + data.alter.forEach(e => (e.number = 99)) // change data + await UPDATE.entity(Order).with(data).where('exists alter') const selectAfterChange = await cds.run(CQL`SELECT from Order { ID, alter { * } } where exists alter`) expect(selectAfterChange[0]).to.deep.eql(data) }) + + test('insert as select', async () => { + const { Alter, ASC } = cds.entities + // fill other table first + await cds.run(INSERT({ ID: 1, alias: 42 }).into(ASC)) + const insert = INSERT.into(Alter) + .columns(['ID', 'number']) + .as( + SELECT.from(ASC) + .columns(['ID', 'alias']) + .where({ ref: ['alias'] }, '=', { val: 42 }), + ) + await cds.run(insert) + const select = await SELECT.from(Alter).where('number = 42') + expect(select[0]).to.eql({ ID: 1, number: 42, order_ID: null }) + }) }) diff --git a/test/compliance/resources/db/keywords/index.cds b/test/compliance/resources/db/keywords/index.cds index de5bccbaf..af441bc3c 100644 --- a/test/compliance/resources/db/keywords/index.cds +++ b/test/compliance/resources/db/keywords/index.cds @@ -9,3 +9,7 @@ entity Alter { number: Integer; order: Association to Order; } +entity ASC { + key ID : Integer; + alias: Integer; +} From 0b3b4e598fc827edf51c3a3ca4346fa01dea0319 Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Fri, 23 Feb 2024 22:41:20 +0100 Subject: [PATCH 7/8] less code --- test/compliance/keywords.test.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/compliance/keywords.test.js b/test/compliance/keywords.test.js index 7aac512f2..1bfb1e02f 100644 --- a/test/compliance/keywords.test.js +++ b/test/compliance/keywords.test.js @@ -35,15 +35,14 @@ describe('keywords', () => { const { Alter, ASC } = cds.entities // fill other table first await cds.run(INSERT({ ID: 1, alias: 42 }).into(ASC)) - const insert = INSERT.into(Alter) + await INSERT.into(Alter) .columns(['ID', 'number']) .as( SELECT.from(ASC) .columns(['ID', 'alias']) .where({ ref: ['alias'] }, '=', { val: 42 }), ) - await cds.run(insert) - const select = await SELECT.from(Alter).where('number = 42') - expect(select[0]).to.eql({ ID: 1, number: 42, order_ID: null }) + const select = await SELECT.from(Alter).where('number = 42') + expect(select[0]).to.eql({ ID: 1, number: 42, order_ID: null }) }) }) From b4b23131da04ce70ff2a5c3118b7b3e63bad788a Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Fri, 23 Feb 2024 22:43:55 +0100 Subject: [PATCH 8/8] test comment --- test/compliance/resources/db/keywords/index.cds | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compliance/resources/db/keywords/index.cds b/test/compliance/resources/db/keywords/index.cds index af441bc3c..636cb50d9 100644 --- a/test/compliance/resources/db/keywords/index.cds +++ b/test/compliance/resources/db/keywords/index.cds @@ -1,4 +1,4 @@ -// ORDER / ALTER is a reserved word in the ANSI SQL standard +// ORDER / ALTER / ASC / NUMBER are reserved words in ANSI SQL standard entity Order { key ID : Integer; alter: composition of many Alter on alter.order = $self;