Skip to content

Commit

Permalink
fix: pseudo paths in expands (#896)
Browse files Browse the repository at this point in the history
pseudo paths were leading to a dump if they were
used in an expand on an association which follows them in its filter
expression.

We need to revisit #617, as there is a lot of duplicate logic in two
main functions of `infer` --> get rid of
`attachRefLinksToArg`!

---------

Co-authored-by: Johannes Vogel <31311694+johannes-vogel@users.noreply.github.com>
  • Loading branch information
patricebender and johannes-vogel authored Nov 18, 2024
1 parent e341af4 commit 014c50c
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 6 deletions.
5 changes: 5 additions & 0 deletions db-service/lib/cqn4sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -1868,13 +1868,18 @@ function cqn4sql(originalQuery, model) {
value: [],
writable: true,
})
let pseudoPath = false
ref.reduce((prev, res, i) => {
if (res === '$self')
// next is resolvable in entity
return prev
if (res in pseudos.elements) {
pseudoPath = true
thing.$refLinks.push({ definition: pseudos.elements[res], target: pseudos })
return pseudos.elements[res]
} else if (pseudoPath) {
thing.$refLinks.push({ definition: {}, target: pseudos })
return prev?.elements[res]
}
const definition =
prev?.elements?.[res] || getDefinition(prev?.target)?.elements[res] || pseudos.elements[res]
Expand Down
17 changes: 13 additions & 4 deletions db-service/lib/infer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,17 @@ function infer(originalQuery, model) {
if (!ref) return
init$refLinks(arg)
let i = 0
let pseudoPath = false
for (const step of ref) {
const id = step.id || step
if (i === 0) {
// infix filter never have table alias
// we need to search for first step in ´model.definitions[infixAlias]`
if ($baseLink) {
if (id in pseudos.elements) {
// pseudo path
arg.$refLinks.push({ definition: pseudos.elements[id], target: pseudos })
pseudoPath = true // only first path step must be well defined
} else if ($baseLink) {
// infix filter never have table alias
// we need to search for first step in ´model.definitions[infixAlias]`
const { definition } = $baseLink
const elements = getDefinition(definition.target)?.elements || definition.elements
const e = elements?.[id] || cds.error`"${id}" not found in the elements of "${definition.name}"`
Expand All @@ -201,11 +206,15 @@ function infer(originalQuery, model) {
const definition = getDefinition(id) || cds.error`"${id}" not found in the definitions of your model`
arg.$refLinks[0] = { definition, target: definition }
}
} else if (arg.ref[0] === '$user' && pseudoPath) {
// `$user.some.unknown.element` -> no error
arg.$refLinks.push({ definition: {}, target: pseudos })
} else {
const recent = arg.$refLinks[i - 1]
const { elements } = getDefinition(recent.definition.target) || recent.definition
const e = elements[id]
if (!e) throw new Error(`"${id}" not found in the elements of "${arg.$refLinks[i - 1].definition.name}"`)
const notFoundIn = pseudoPath ? arg.ref[i - 1] : getFullPathForLinkedArg(arg)
if (!e) throw new Error(`"${id}" not found in the elements of "${notFoundIn}"`)
arg.$refLinks.push({ definition: e, target: getDefinition(e.target) || e })
}
arg.$refLinks[i].alias = !ref[i + 1] && arg.as ? arg.as : id.split('.').pop()
Expand Down
2 changes: 1 addition & 1 deletion db-service/test/bookshop/db/booksWithExpr.cds
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,5 @@ entity VariableReplacements {
key ID: Integer;
author: Association to Authors;
// with variable replacements
authorAlive = author[dateOfBirth <= $now and dateOfDeath >= $now];
authorAlive = author[dateOfBirth <= $now and dateOfDeath >= $now and $user.unknown.foo.bar = 'Bob'];
}
18 changes: 17 additions & 1 deletion db-service/test/cqn4sql/calculated-elements.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,7 @@ describe('Unfolding calculated elements in other places', () => {
const q = CQL`SELECT from booksCalc.VariableReplacements { ID, authorAlive.firstName }`
const expected = CQL`SELECT from booksCalc.VariableReplacements as VariableReplacements
left join booksCalc.Authors as authorAlive on ( authorAlive.ID = VariableReplacements.author_ID )
and ( authorAlive.dateOfBirth <= $now and authorAlive.dateOfDeath >= $now )
and ( authorAlive.dateOfBirth <= $now and authorAlive.dateOfDeath >= $now and $user.unknown.foo.bar = 'Bob' )
{
VariableReplacements.ID,
authorAlive.firstName as authorAlive_firstName
Expand All @@ -991,6 +991,22 @@ describe('Unfolding calculated elements in other places', () => {
}`
expect(cqn4sql(q, model)).to.deep.equal(expected)
})

it('with expand', () => {
let query = cqn4sql(CQL`SELECT from booksCalc.VariableReplacements { ID, authorAlive { ID } }`, model)
const expected = CQL`SELECT from booksCalc.VariableReplacements as VariableReplacements {
VariableReplacements.ID,
(
SELECT from booksCalc.Authors as authorAlive
{
authorAlive.ID,
}
where (authorAlive.ID = VariableReplacements.author_ID)
and ( authorAlive.dateOfBirth <= $now and authorAlive.dateOfDeath >= $now and $user.unknown.foo.bar = 'Bob' )
) as authorAlive
}`
expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected)
})
})

describe('Unfolding calculated elements ... misc', () => {
Expand Down

0 comments on commit 014c50c

Please sign in to comment.