Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1,254 changes: 0 additions & 1,254 deletions db-service/test/cqn4sql/calculated-elements.test.js

This file was deleted.

65 changes: 65 additions & 0 deletions db-service/test/cqn4sql/calculated-elements.test/basics.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict'

const cds = require('@sap/cds')
const { loadModel } = require('../helpers/model')
const { expectCqn } = require('../helpers/expectCqn')

let cqn4sql = require('../../../lib/cqn4sql')

describe('Unfolding calculated elements - basics', () => {
before(async () => {
const model = await loadModel()
const orig = cqn4sql
cqn4sql = (q, m) => orig(q, m ?? model)
})

it('simple reference', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, stock2 }`)
const expected = cds.ql`
SELECT from booksCalc.Books as Books {
Books.ID,
Books.stock as stock2
}`
expectCqn(transformed).to.equal(expected)
})

it('simple val', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Authors as Authors { ID, IBAN }`)
const expected = cds.ql`
SELECT from booksCalc.Authors as Authors {
Authors.ID,
'DE' || Authors.checksum || Authors.sortCode || Authors.accountNumber as IBAN
}`
expectCqn(transformed).to.equal(expected)
})

it('directly', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, area }`)
const expected = cds.ql`
SELECT from booksCalc.Books as Books {
Books.ID,
Books.length * Books.width as area
}`
expectCqn(transformed).to.equal(expected)
})

it('in expression', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, stock * area as f }`)
const expected = cds.ql`
SELECT from booksCalc.Books as Books {
Books.ID,
Books.stock * ( Books.length * Books.width ) as f
}`
expectCqn(transformed).to.equal(expected)
})

it('in function', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, round(area, 2) as f }`)
const expected = cds.ql`
SELECT from booksCalc.Books as Books {
Books.ID,
round(Books.length * Books.width, 2) as f
}`
expectCqn(transformed).to.equal(expected)
})
})
39 changes: 39 additions & 0 deletions db-service/test/cqn4sql/calculated-elements.test/drafts.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict'

const cds = require('@sap/cds')
const { loadModel } = require('../helpers/model')
const { expectCqn, expect } = require('../helpers/expectCqn')

let cqn4sql = require('../../../lib/cqn4sql')

describe('calculated elements in draft enabled entities', () => {
before(async () => {
const model = await loadModel({ flat: true }, [__dirname + '/../../bookshop/srv/calc-elem-service'])
const orig = cqn4sql
cqn4sql = (q, m) => orig(q, m ?? model)
})

it('keeps param: false for query against active entries', () => {
const transformed = cqn4sql(cds.ql`SELECT from CalcService.Orders as Orders { ID, expensive }`)
const expected = cds.ql`SELECT from CalcService.Orders as Orders {
Orders.ID,
case when Orders.amount > 10 then 1 else 0 end as expensive
}`
expectCqn(transformed).to.equal(expected)
expect(transformed.SELECT.columns[1].xpr[4].param).to.be.false
expect(transformed.SELECT.columns[1].xpr[6].param).to.be.false
expect(transformed.SELECT.columns[1].xpr[8].param).to.be.false
})

it('keeps param: false for query against draft entries', () => {
const transformed = cqn4sql(cds.ql`SELECT from CalcService.Orders.drafts as Orders { ID, expensive }`)
const expected = cds.ql`SELECT from CalcService.Orders.drafts as Orders {
Orders.ID,
case when Orders.amount > 10 then 1 else 0 end as expensive
}`
expectCqn(transformed).to.equal(expected)
expect(transformed.SELECT.columns[1].xpr[4].param).to.be.false
expect(transformed.SELECT.columns[1].xpr[6].param).to.be.false
expect(transformed.SELECT.columns[1].xpr[8].param).to.be.false
})
})
87 changes: 87 additions & 0 deletions db-service/test/cqn4sql/calculated-elements.test/exists.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use strict'

const cds = require('@sap/cds')
const { loadModel } = require('../helpers/model')
const { expectCqn, expect } = require('../helpers/expectCqn')

let cqn4sql = require('../../../lib/cqn4sql')

describe('Unfolding calculated elements - exists', () => {
before(async () => {
const model = await loadModel()
const orig = cqn4sql
cqn4sql = (q, m) => orig(q, m ?? model)
})

it('exists cannot leverage calculated elements which ends in string', () => {
// at the leaf of a where exists path, there must be an association
expect(() => cqn4sql(cds.ql`SELECT from booksCalc.Books { ID } where exists youngAuthorName`)).to.throw(
`Expecting path “youngAuthorName” following “EXISTS” predicate to end with association/composition, found “cds.String”`,
)
})

it('exists cannot leverage calculated elements which is an expression', () => {
// at the leaf of a where exists path, there must be an association
expect(() => cqn4sql(cds.ql`SELECT from booksCalc.Books { ID } where exists authorFullName`)).to.throw(
`Expecting path “authorFullName” following “EXISTS” predicate to end with association/composition, found “expression”`,
)
})

it('exists cannot leverage calculated elements w/ path expressions', () => {
// at the leaf of a where exists path, there must be an association
expect(() =>
cqn4sql(cds.ql`SELECT from booksCalc.Books { ID } where exists author.books.youngAuthorName`),
).to.throw('Expecting path “author.books.youngAuthorName” following “EXISTS” predicate to end with association/composition, found “cds.String”')
})

it('exists cannot leverage calculated elements in CASE', () => {
expect(() =>
cqn4sql(cds.ql`SELECT from booksCalc.Books {
ID,
case when exists youngAuthorName then 'yes'
else 'no'
end as x
}`),
).to.throw('Expecting path “youngAuthorName” following “EXISTS” predicate to end with association/composition, found “cds.String”')
})

it('scoped query cannot leverage calculated elements', () => {
// at the leaf of a where exists path, there must be an association
expect(() => cqn4sql(cds.ql`SELECT from booksCalc.Books:youngAuthorName { ID }`)).to.throw(
'Query source must be a an entity or an association',
)
})

describe('calculated elements with exists accessed through association', () => {
it('accessing parent calc element with exists through association produces correct JOIN', () => {
const transformed = cqn4sql(cds.ql`SELECT from existsInCalcElement.Tasks as Tasks { ID, isUserNotMember }`)
const expected = cds.ql`
SELECT from existsInCalcElement.Tasks as Tasks
left join existsInCalcElement.Projects as project on project.ID = Tasks.project_ID
{
Tasks.ID,
(case when (case when not exists (
SELECT 1 from existsInCalcElement.Members as $m
where $m.project_ID = project.ID
and $m.userID = $user.id
) then true else false end) = true then true else false end) as isUserNotMember
}`
expectCqn(transformed).to.equal(expected)
})

it('parent calc element with exists directly on entity needs no JOIN', () => {
const transformed = cqn4sql(cds.ql`SELECT from existsInCalcElement.Projects as Projects { ID, isUserNotMember }`)
const expected = cds.ql`
SELECT from existsInCalcElement.Projects as Projects
{
Projects.ID,
(case when not exists (
SELECT 1 from existsInCalcElement.Members as $m
where $m.project_ID = Projects.ID
and $m.userID = $user.id
) then true else false end) as isUserNotMember
}`
expectCqn(transformed).to.equal(expected)
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use strict'

const cds = require('@sap/cds')
const { loadModel } = require('../helpers/model')
const { expectCqn } = require('../helpers/expectCqn')

let cqn4sql = require('../../../lib/cqn4sql')

describe('Unfolding calculated elements - filters', () => {
before(async () => {
const model = await loadModel()
const orig = cqn4sql
cqn4sql = (q, m) => orig(q, m ?? model)
})

it('in filter', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Authors as Authors { ID, books[area >17].title }`)
// intermediate:
// SELECT from booksCalc.Authors { ID, books[(length * width) > 1].title }
const expected = cds.ql`
SELECT from booksCalc.Authors as Authors
left outer join booksCalc.Books as books on books.author_ID = Authors.ID
and (books.length * books.width) > 17
{
Authors.ID,
books.title as books_title
}`
expectCqn(transformed).to.equal(expected)
})

it('calc elem contains association with filter', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Authors as Authors { ID, addressTextFilter }`)
// intermediate:
// SELECT from booksCalc.Authors { ID, address[number * 2 > 17].{street || ', ' || city} }
const expected = cds.ql`
SELECT from booksCalc.Authors as Authors
left outer join booksCalc.Addresses as address on address.ID = Authors.address_ID
and (address.number * 2) > 17
{
Authors.ID,
address.street || ', ' || address.city as addressTextFilter
}`
expectCqn(transformed).to.equal(expected)
})

it('calculated element has other calc element in infix filter', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books {
ID,
youngAuthorName
}`)
const expected = cds.ql`
SELECT from booksCalc.Books as Books
left join booksCalc.Authors as author on author.ID = Books.author_ID
and years_between(author.dateOfBirth, author.dateOfDeath) < 50
{
Books.ID,
author.firstName || ' ' || author.lastName as youngAuthorName
}
`
expectCqn(transformed).to.equal(expected)
})

it('in filter in path in FROM', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Authors[name like 'A%'].books[storageVolume < 4] { ID }`)
const expected = cds.ql`
SELECT from booksCalc.Books as $b {
$b.ID
} where exists (select 1 from booksCalc.Authors as $A
where $A.ID = $b.author_ID
and ($A.firstName || ' ' || $A.lastName) like 'A%')
and ($b.stock * (($b.length * $b.width) * $b.height)) < 4
`
expectCqn(transformed).to.equal(expected)
})

it('in filter in where exists', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Authors { ID } where exists books[area < 13]`)
const expected = cds.ql`
SELECT from booksCalc.Authors as $A { $A.ID }
where exists (
select 1 from booksCalc.Books as $b where $b.author_ID = $A.ID
and ($b.length * $b.width) < 13
)
`
expectCqn(transformed).to.equal(expected)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
'use strict'

const cds = require('@sap/cds')
const { loadModel } = require('../helpers/model')
const { expectCqn } = require('../helpers/expectCqn')

let cqn4sql = require('../../../lib/cqn4sql')

describe('Unfolding calculated elements - functions', () => {
before(async () => {
const model = await loadModel()
const orig = cqn4sql
cqn4sql = (q, m) => orig(q, m ?? model)
})

it('in function with named param', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Authors as Authors { ID, ageNamedParams as f }`)
const expected = cds.ql`
SELECT from booksCalc.Authors as Authors {
Authors.ID,
years_between(DOB => Authors.dateOfBirth, DOD => Authors.dateOfDeath) as f
}`
expectCqn(transformed).to.equal(expected)
})

it('calc elem is function', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, ctitle }`)
const expected = cds.ql`
SELECT from booksCalc.Books as Books {
Books.ID,
substring(Books.title, 3, Books.stock) as ctitle
}`
expectCqn(transformed).to.equal(expected)
})

it('calc elem is xpr with multiple functions as args', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, authorAgeNativePG }`)
const expected = cds.ql`
SELECT from booksCalc.Books as Books
left join booksCalc.Authors as author on author.ID = Books.author_ID
{
Books.ID,
DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) as authorAgeNativePG
}`
expectCqn(transformed).to.equal(expected)
})

it('calc elem is xpr with nested xpr which has multiple functions as args', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, authorAgeInDogYears }`)
const expected = cds.ql`
SELECT from booksCalc.Books as Books
left join booksCalc.Authors as author on author.ID = Books.author_ID
{
Books.ID,
( DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) ) * 7 as authorAgeInDogYears
}`
expectCqn(transformed).to.equal(expected)
})

it('calc elem is xpr with multiple functions as args - back and forth', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, author.books.authorAgeNativePG }`)
const expected = cds.ql`
SELECT from booksCalc.Books as Books
left join booksCalc.Authors as author on author.ID = Books.author_ID
left join booksCalc.Books as books2 on books2.author_ID = author.ID
left join booksCalc.Authors as author2 on author2.ID = books2.author_ID
{
Books.ID,
DATE_PART('year', author2.dateOfDeath) - DATE_PART('year', author2.dateOfBirth) as author_books_authorAgeNativePG
}`
expectCqn(transformed).to.equal(expected)
})

it('calc elem is function, nested in direct expression', () => {
const transformed = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, ctitle || title as f }`)
const expected = cds.ql`
SELECT from booksCalc.Books as Books {
Books.ID,
substring(Books.title, 3, Books.stock) || Books.title as f
}`
expectCqn(transformed).to.equal(expected)
})
})
Loading