Skip to content

Commit

Permalink
fix(calculated elements): path expressions in func.args within xpr (
Browse files Browse the repository at this point in the history
#321)

the `args` of the `func` have not been considered, now the correct `join` is produced
  • Loading branch information
patricebender authored Oct 26, 2023
1 parent 9c9be9e commit cee25e3
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 12 deletions.
14 changes: 10 additions & 4 deletions db-service/lib/infer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -873,20 +873,26 @@ function infer(originalQuery, model = cds.context?.model || cds.model) {
if (leafOfCalculatedElementRef.value) mergePathsIntoJoinTree(leafOfCalculatedElementRef.value, basePath)

mergePathIfNecessary(basePath, arg)
} else if (arg.xpr) {
arg.xpr.forEach(step => {
} else if (arg.xpr || arg.args) {
const prop = arg.xpr ? 'xpr' : 'args'
arg[prop].forEach(step => {
const subPath = { $refLinks: [...basePath.$refLinks], ref: [...basePath.ref] }
if (step.ref) {
const subPath = { $refLinks: [...basePath.$refLinks], ref: [...basePath.ref] }
step.$refLinks.forEach((link, i) => {
const { definition } = link
if (definition.value) {
mergePathsIntoJoinTree(definition.value)
mergePathsIntoJoinTree(definition.value, subPath)
} else {
subPath.$refLinks.push(link)
subPath.ref.push(step.ref[i])
}
})
mergePathIfNecessary(subPath, step)
} else if (step.args || step.xpr) {
const nestedProp = step.xpr ? 'xpr' : 'args'
step[nestedProp].forEach(a => {
mergePathsIntoJoinTree(a, subPath)
})
}
})
}
Expand Down
5 changes: 5 additions & 0 deletions db-service/test/bookshop/db/booksWithExpr.cds
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,15 @@ entity Books {
authorAdrText = author.addressText;

authorAge: Integer = years_between( author.sortCode, author.sortCode );
authorAgeNativePG: Integer = DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth);

// calculated element is `xpr` which has subsequent `xpr`
authorAgeInDogYears: Integer = ( DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) ) * 7;
}

entity Authors {
key ID : Integer;

firstName : String;
lastName : String;

Expand Down
2 changes: 2 additions & 0 deletions db-service/test/cds-infer/calculated-elements.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ describe('Infer types of calculated elements in select list', () => {
authorAdrText: Books.elements.authorAdrText,
authorAge: Books.elements.authorAge,
youngAuthorName: Books.elements.youngAuthorName,
authorAgeNativePG: Books.elements.authorAgeNativePG,
authorAgeInDogYears: Books.elements.authorAgeInDogYears,
})
})
})
63 changes: 55 additions & 8 deletions db-service/test/cqn4sql/calculated-elements.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,38 @@ describe('Unfolding calculated elements in select list', () => {
}`
expect(query).to.deep.equal(expected)
})
it('calc elem is xpr with multiple functions as args', () => {
let query = cqn4sql(CQL`SELECT from booksCalc.Books { ID, authorAgeNativePG }`, model)
const expected = CQL`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
}`
expect(query).to.deep.equal(expected)
})
it('calc elem is xpr with nested xpr which has multiple functions as args', () => {
let query = cqn4sql(CQL`SELECT from booksCalc.Books { ID, authorAgeInDogYears }`, model)
const expected = CQL`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
}`
expect(query).to.deep.equal(expected)
})
it('calc elem is xpr with multiple functions as args - back and forth', () => {
let query = cqn4sql(CQL`SELECT from booksCalc.Books { ID, author.books.authorAgeNativePG }`, model)
const expected = CQL`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
}`
expect(query).to.deep.equal(expected)
})

it('calc elem is function, nested in direct expression', () => {
let query = cqn4sql(CQL`SELECT from booksCalc.Books { ID, ctitle || title as f }`, model)
Expand Down Expand Up @@ -434,7 +466,10 @@ describe('Unfolding calculated elements in select list', () => {
author.firstName || ' ' || author.lastName as authorFullName,
(author.firstName || ' ' || author.lastName) || ' ' || (address.street || ', ' || address.city) as authorFullNameWithAddress,
address.street || ', ' || address.city as authorAdrText,
years_between( author.sortCode, author.sortCode ) as authorAge
years_between( author.sortCode, author.sortCode ) as authorAge,
DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) as authorAgeNativePG,
( DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) ) * 7 as authorAgeInDogYears
}`
expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected)
})
Expand Down Expand Up @@ -466,7 +501,10 @@ describe('Unfolding calculated elements in select list', () => {
author.firstName || ' ' || author.lastName as authorFullName,
(author.firstName || ' ' || author.lastName) || ' ' || (address.street || ', ' || address.city) as authorFullNameWithAddress,
address.street || ', ' || address.city as authorAdrText,
years_between( author.sortCode, author.sortCode ) as authorAge
years_between( author.sortCode, author.sortCode ) as authorAge,
DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) as authorAgeNativePG,
( DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) ) * 7 as authorAgeInDogYears
}`
expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected)
})
Expand Down Expand Up @@ -498,7 +536,10 @@ describe('Unfolding calculated elements in select list', () => {
author.firstName || ' ' || author.lastName as authorFullName,
(author.firstName || ' ' || author.lastName) || ' ' || (address.street || ', ' || address.city) as authorFullNameWithAddress,
address.street || ', ' || address.city as authorAdrText,
years_between( author.sortCode, author.sortCode ) as authorAge
years_between( author.sortCode, author.sortCode ) as authorAge,
DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) as authorAgeNativePG,
( DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) ) * 7 as authorAgeInDogYears
}`
expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected)
})
Expand Down Expand Up @@ -616,9 +657,9 @@ describe('Unfolding calculated elements in select list', () => {
it('exists cannot leverage calculated elements w/ path expressions', () => {
// at the leaf of a where exists path, there must be an association
// calc elements can't end in an association, hence this does not work, yet.
expect(() => cqn4sql(CQL`SELECT from booksCalc.Books { ID } where exists author.books.youngAuthorName`, model)).to.throw(
'Calculated elements cannot be used in “exists” predicates in: “exists author.books.youngAuthorName”',
)
expect(() =>
cqn4sql(CQL`SELECT from booksCalc.Books { ID } where exists author.books.youngAuthorName`, model),
).to.throw('Calculated elements cannot be used in “exists” predicates in: “exists author.books.youngAuthorName”')
})

it('exists cannot leverage calculated elements in CASE', () => {
Expand Down Expand Up @@ -681,7 +722,10 @@ describe('Unfolding calculated elements in select list', () => {
(author2.firstName || ' ' || author2.lastName) || ' ' || (address.street || ', ' || address.city) as authorFullNameWithAddress,
address.street || ', ' || address.city as authorAdrText,
years_between( author2.sortCode, author2.sortCode ) as authorAge
years_between( author2.sortCode, author2.sortCode ) as authorAge,
DATE_PART('year', author2.dateOfDeath) - DATE_PART('year', author2.dateOfBirth) as authorAgeNativePG,
( DATE_PART('year', author2.dateOfDeath) - DATE_PART('year', author2.dateOfBirth) ) * 7 as authorAgeInDogYears
} where Authors.ID = books.author_ID
) as books
}`
Expand Down Expand Up @@ -722,7 +766,10 @@ describe('Unfolding calculated elements in select list', () => {
(author.firstName || ' ' || author.lastName) || ' ' || (address.street || ', ' || address.city) as authorFullNameWithAddress,
address.street || ', ' || address.city as authorAdrText,
years_between( author.sortCode, author.sortCode ) as authorAge
years_between( author.sortCode, author.sortCode ) as authorAge,
DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) as authorAgeNativePG,
( DATE_PART('year', author.dateOfDeath) - DATE_PART('year', author.dateOfBirth) ) * 7 as authorAgeInDogYears
} where Authors.ID = books.author_ID
) as books
}`
Expand Down

0 comments on commit cee25e3

Please sign in to comment.