Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: always generate unique subquery aliases #435

Merged
merged 7 commits into from
Feb 1, 2024
Merged
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
19 changes: 11 additions & 8 deletions db-service/lib/cqn4sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -388,13 +388,6 @@ function cqn4sql(originalQuery, model = cds.context?.model || cds.model) {
return transformedColumns

function handleSubquery(col) {
if (!col.SELECT.from.as) {
const uniqueSubqueryAlias = inferred.joinTree.addNextAvailableTableAlias(
getLastStringSegment(col.SELECT.from.ref[col.SELECT.from.ref.length - 1]),
originalQuery.outerQueries,
)
Object.defineProperty(col.SELECT.from, 'uniqueSubqueryAlias', { value: uniqueSubqueryAlias })
}
transformedColumns.push(() => {
const res = transformSubquery(col)
if (col.as) res.as = col.as
Expand Down Expand Up @@ -442,7 +435,7 @@ function cqn4sql(originalQuery, model = cds.context?.model || cds.model) {
}

let columnAlias = col.as || (col.isJoinRelevant ? col.flatName : null)
const refNavigation = col.ref.slice(col.ref[0] === tableAlias ? 1 : 0).join('_')
const refNavigation = col.ref.slice(col.$refLinks[0].definition.kind !== 'element' ? 1 : 0).join('_')
if (!columnAlias && col.flatName && col.flatName !== refNavigation) columnAlias = refNavigation

if (col.$refLinks.some(link => link.definition._target?.['@cds.persistence.skip'] === true)) return
Expand Down Expand Up @@ -918,7 +911,17 @@ function cqn4sql(originalQuery, model = cds.context?.model || cds.model) {
Object.defineProperty(q, 'outerQueries', { value: outerQueries })
}
if (isLocalized(inferred.target)) q.SELECT.localized = true
if (q.SELECT.from.ref && !q.SELECT.from.as) assignUniqueSubqueryAlias()
return cqn4sql(q, model)

function assignUniqueSubqueryAlias() {
if (q.SELECT.from.uniqueSubqueryAlias) return
const uniqueSubqueryAlias = inferred.joinTree.addNextAvailableTableAlias(
getLastStringSegment(q.SELECT.from.ref[q.SELECT.from.ref.length - 1]),
originalQuery.outerQueries,
)
Object.defineProperty(q.SELECT.from, 'uniqueSubqueryAlias', { value: uniqueSubqueryAlias })
}
}

/**
Expand Down
3 changes: 0 additions & 3 deletions db-service/lib/infer/join-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,6 @@ class JoinTree {
Object.entries(sources).forEach(entry => {
const alias = this.addNextAvailableTableAlias(entry[0])
this._roots.set(alias, new Root(entry))
if (entry[1].sources)
// respect outer aliases
this.addAliasesOfSubqueryInFrom(entry[1].sources)
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ exports[`expressions with complex xpr 1`] = `

exports[`expressions with exists 1`] = `
{
"sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE exists (SELECT Foo2.name FROM Foo2 as Foo2) or not exists (SELECT Foo2.name FROM Foo2 as Foo2)",
"sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE exists (SELECT Foo2.name FROM Foo2 as Foo2) or not exists (SELECT Foo22.name FROM Foo2 as Foo22)",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this Foo22 looked wrong at a first glimpse. However, as we SELECT FROM Foo2 the 2nd Foo2 alias becomes Foo22, so all good :)

"values": [],
}
`;
Expand Down
6 changes: 3 additions & 3 deletions db-service/test/cqn2sql/__snapshots__/select.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ exports[`cqn2sql WHERE entries where with place holder 1`] = `
}
`;

exports[`cqn2sql WHERE select with a nested select in a complex where 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo WHERE ( Foo.x + 1 ) < 9 AND Foo.x IN (SELECT Foo.a FROM Foo as Foo WHERE Foo.x < 9)"`;
exports[`cqn2sql WHERE select with a nested select in a complex where 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo WHERE ( Foo.x + 1 ) < 9 AND Foo.x IN (SELECT Foo2.a FROM Foo as Foo2 WHERE Foo2.x < 9)"`;

exports[`cqn2sql WHERE select with a nested select in where 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo WHERE Foo.x IN (SELECT Foo.a FROM Foo as Foo WHERE Foo.x < 9)"`;
exports[`cqn2sql WHERE select with a nested select in where 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo WHERE Foo.x IN (SELECT Foo2.a FROM Foo as Foo2 WHERE Foo2.x < 9)"`;

exports[`cqn2sql WHERE where with partial cqn 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE (Foo.x = 9)"`;

Expand Down Expand Up @@ -128,7 +128,7 @@ exports[`cqn2sql quoted column aliases select with subselect in exists and colum

exports[`cqn2sql quoted column aliases select with subselect with in and column aliases 1`] = `
{
"sql": "SELECT Foo.a as A,? as ABC,Foo.x + 1 as Xpr1 FROM Foo as Foo WHERE ( Foo.x + 1 ) < 9 AND Foo.x IN (SELECT Foo.a as B,Foo.x - 4 as Xpr2 FROM Foo as Foo WHERE Foo.x < 9)",
"sql": "SELECT Foo.a as A,? as ABC,Foo.x + 1 as Xpr1 FROM Foo as Foo WHERE ( Foo.x + 1 ) < 9 AND Foo.x IN (SELECT Foo2.a as B,Foo2.x - 4 as Xpr2 FROM Foo as Foo2 WHERE Foo2.x < 9)",
"values": [
"abc",
],
Expand Down
10 changes: 5 additions & 5 deletions db-service/test/cqn4sql/assocs2joins.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -926,13 +926,13 @@ describe('subqueries in from', () => {
model,
)
const expected = CQL`SELECT from (
SELECT from bookshop.Books as Books
left outer join bookshop.Authors as author on author.ID = Books.author_ID
{ Books.author_ID, Books.author_ID as a_ID, author.name as author_name }
SELECT from bookshop.Books as Books2
left outer join bookshop.Authors as author on author.ID = Books2.author_ID
{ Books2.author_ID, Books2.author_ID as a_ID, author.name as author_name }
) as Bar
left outer join bookshop.Authors as a on a.ID = Bar.a_ID
left outer join bookshop.Books as books2 on books2.author_ID = a.ID
{ Bar.author_name, books2.descr as a_books_descr}
left outer join bookshop.Books as books on books.author_ID = a.ID
{ Bar.author_name, books.descr as a_books_descr}
`
expect(query).to.deep.equal(expected)
})
Expand Down
4 changes: 2 additions & 2 deletions db-service/test/cqn4sql/table-alias.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ describe('table alias access', () => {
model,
)
expect(query).to.deep.equal(
CQL`SELECT from (SELECT from bookshop.Books as Books { Books.ID, Books.stock }) as Books { Books.ID, Books.stock }`,
CQL`SELECT from (SELECT from bookshop.Books as Books2 { Books2.ID, Books2.stock }) as Books { Books.ID, Books.stock }`,
)
})
it('explicit alias for FROM subquery', () => {
Expand Down Expand Up @@ -930,7 +930,7 @@ describe('table alias access', () => {
where: [
{ list: [{ ref: ['dedication', 'addressee', 'ID'] }] },
'in',
CQL`SELECT Books2.ID from bookshop.Books as Books2 where Books2.ID = 5`,
CQL`SELECT ID from bookshop.Books where ID = 5`,
],
},
],
Expand Down