diff --git a/db-service/lib/cqn4sql.js b/db-service/lib/cqn4sql.js index 6df3af285..7585d5693 100644 --- a/db-service/lib/cqn4sql.js +++ b/db-service/lib/cqn4sql.js @@ -5,7 +5,7 @@ cds.infer.target ??= q => q._target || q.target // instanceof cds.entity ? q._ta const infer = require('./infer') const { computeColumnsToBeSearched } = require('./search') -const { prettyPrintRef, isCalculatedOnRead, isCalculatedElement } = require('./utils') +const { prettyPrintRef, isCalculatedOnRead, isCalculatedElement, getImplicitAlias } = require('./utils') /** * For operators of , this is replaced by comparing all leaf elements with null, combined with and. @@ -63,16 +63,16 @@ function cqn4sql(originalQuery, model) { } // query modifiers can also be defined in from ref leaf infix filter // > SELECT from bookshop.Books[order by price] {ID} - if(inferred.SELECT?.from.ref) { - for(const [key, val] of Object.entries(inferred.SELECT.from.ref.at(-1))) { - if(key in { orderBy: 1, groupBy: 1 }) { - if(inferred.SELECT[key]) inferred.SELECT[key].push(...val) + if (inferred.SELECT?.from.ref) { + for (const [key, val] of Object.entries(inferred.SELECT.from.ref.at(-1))) { + if (key in { orderBy: 1, groupBy: 1 }) { + if (inferred.SELECT[key]) inferred.SELECT[key].push(...val) else inferred.SELECT[key] = val - } else if(key === 'limit') { + } else if (key === 'limit') { // limit defined on the query has precedence - if(!inferred.SELECT.limit) inferred.SELECT.limit = val - } else if(key === 'having') { - if(!inferred.SELECT.having) inferred.SELECT.having = val + if (!inferred.SELECT.limit) inferred.SELECT.limit = val + } else if (key === 'having') { + if (!inferred.SELECT.having) inferred.SELECT.having = val else inferred.SELECT.having.push('and', ...val) } } @@ -818,16 +818,18 @@ function cqn4sql(originalQuery, model) { return _subqueryForGroupBy(column, baseRef, columnAlias) } - // we need to respect the aliases of the outer query, so the columnAlias might not be suitable - // as table alias for the correlated subquery - const uniqueSubqueryAlias = getNextAvailableTableAlias(columnAlias, inferred.outerQueries) + // Alias in expand subquery is derived from but not equal to + // the alias of the column because to account for potential ambiguities + // the alias cannot be addressed anyways + const uniqueSubqueryAlias = getNextAvailableTableAlias(getImplicitAlias(columnAlias), inferred.outerQueries) // `SELECT from Authors { books.genre as genreOfBooks { name } } becomes `SELECT from Books:genre as genreOfBooks` const from = { ref: subqueryFromRef, as: uniqueSubqueryAlias } const subqueryBase = {} const queryModifiers = { ...column } for (const [key, value] of Object.entries(queryModifiers)) { - if (key in { limit: 1, orderBy: 1, groupBy: 1, excluding: 1, where: 1, having: 1, count: 1 }) subqueryBase[key] = value + if (key in { limit: 1, orderBy: 1, groupBy: 1, excluding: 1, where: 1, having: 1, count: 1 }) + subqueryBase[key] = value } const subquery = { @@ -871,6 +873,7 @@ function cqn4sql(originalQuery, model) { const existsSubqueryAlias = recent[existsIndex + 1].SELECT.from.as if (existsSubqueryAlias === x.ref?.[0]) return { ref: [outer, ...x.ref.slice(1)] } if (x.xpr) x.xpr = x.xpr.map(replaceAliasWithSubqueryAlias) + if (x.args) x.args = x.args.map(replaceAliasWithSubqueryAlias) return x } return subq @@ -900,37 +903,39 @@ function cqn4sql(originalQuery, model) { // expand with wildcard vanishes as expand is part of the group by (OData $apply + $expand) return null } - const expandedColumns = column.expand.flatMap(expand => { - if (!expand.ref) return expand - const fullRef = [...baseRef, ...expand.ref] - - if (expand.expand) { - const nested = _subqueryForGroupBy(expand, fullRef, expand.as || expand.ref.map(idOnly).join('_')) - if(nested) { - setElementOnColumns(nested, expand.element) - elements[expand.as || expand.ref.map(idOnly).join('_')] = nested + const expandedColumns = column.expand + .flatMap(expand => { + if (!expand.ref) return expand + const fullRef = [...baseRef, ...expand.ref] + + if (expand.expand) { + const nested = _subqueryForGroupBy(expand, fullRef, expand.as || expand.ref.map(idOnly).join('_')) + if (nested) { + setElementOnColumns(nested, expand.element) + elements[expand.as || expand.ref.map(idOnly).join('_')] = nested + } + return nested } - return nested - } - const groupByRef = groupByLookup.get(fullRef.map(refWithConditions).join('.')) - if (!groupByRef) { - throw new Error( - `The expanded column "${fullRef.map(refWithConditions).join('.')}" must be part of the group by clause`, - ) - } + const groupByRef = groupByLookup.get(fullRef.map(refWithConditions).join('.')) + if (!groupByRef) { + throw new Error( + `The expanded column "${fullRef.map(refWithConditions).join('.')}" must be part of the group by clause`, + ) + } - const copy = Object.create(groupByRef) - // always alias for this special case, so that they nested element names match the expected result structure - // otherwise we'd get `author { .author_ID }`, but we need `author { .author_ID as ID }` - copy.as = expand.as || expand.ref.at(-1) - const tableAlias = getTableAlias(copy) - const res = getFlatColumnsFor(copy, { tableAlias }) - res.forEach(c => { - elements[c.as || c.ref.at(-1)] = c.element + const copy = Object.create(groupByRef) + // always alias for this special case, so that they nested element names match the expected result structure + // otherwise we'd get `author { .author_ID }`, but we need `author { .author_ID as ID }` + copy.as = expand.as || expand.ref.at(-1) + const tableAlias = getTableAlias(copy) + const res = getFlatColumnsFor(copy, { tableAlias }) + res.forEach(c => { + elements[c.as || c.ref.at(-1)] = c.element + }) + return res }) - return res - }).filter(c => c) + .filter(c => c) if (expandedColumns.length === 0) { return null @@ -1074,7 +1079,7 @@ function cqn4sql(originalQuery, model) { if (q.SELECT.from.uniqueSubqueryAlias) return const last = q.SELECT.from.ref.at(-1) const uniqueSubqueryAlias = inferred.joinTree.addNextAvailableTableAlias( - getLastStringSegment(last.id || last), + getImplicitAlias(last.id || last), inferred.outerQueries, ) Object.defineProperty(q.SELECT.from, 'uniqueSubqueryAlias', { value: uniqueSubqueryAlias }) @@ -1403,7 +1408,7 @@ function cqn4sql(originalQuery, model) { j = nextAssocIndex } - const as = getNextAvailableTableAlias(getLastStringSegment(next.alias)) + const as = getNextAvailableTableAlias(getImplicitAlias(next.alias)) next.alias = as if (!next.definition.target) { let type = next.definition.type @@ -1699,12 +1704,12 @@ function cqn4sql(originalQuery, model) { function _transformFrom() { if (typeof from === 'string') { // normalize to `ref`, i.e. for `UPDATE.entity('bookshop.Books')` - return { transformedFrom: { ref: [from], as: getLastStringSegment(from) } } + return { transformedFrom: { ref: [from], as: getImplicitAlias(from) } } } transformedFrom.as = from.uniqueSubqueryAlias || from.as || - getLastStringSegment(transformedFrom.$refLinks[transformedFrom.$refLinks.length - 1].definition.name) + getImplicitAlias(transformedFrom.$refLinks[transformedFrom.$refLinks.length - 1].definition.name) const whereExistsSubSelects = [] const filterConditions = [] const refReverse = [...from.ref].reverse() @@ -1726,14 +1731,17 @@ function cqn4sql(originalQuery, model) { .findIndex(rl => rl.definition.isAssociation || rl.definition.kind === 'entity') next = $refLinksReverse[nextStepIndex] } - let as = getLastStringSegment(next.alias) + let as = getImplicitAlias(next.alias) /** * for an `expand` subquery, we do not need to add * the table alias of the `expand` host to the join tree * --> This is an artificial query, which will later be correlated * with the main query alias. see @function expandColumn() + * There is one exception: + * - if current and next have the same alias, we need to assign a new alias to the next + * */ - if (!(inferred.SELECT?.expand === true)) { + if (!(inferred.SELECT?.expand === true && current.alias.toLowerCase() !== as.toLowerCase())) { as = getNextAvailableTableAlias(as) } next.alias = as @@ -1920,9 +1928,11 @@ function cqn4sql(originalQuery, model) { }) let pseudoPath = false ref.reduce((prev, res, i) => { - if (res === '$self') + if (res === '$self') { // next is resolvable in entity + thing.$refLinks.push({ definition: prev, target: prev }) return prev + } if (res in pseudos.elements) { pseudoPath = true thing.$refLinks.push({ definition: pseudos.elements[res], target: pseudos }) @@ -1934,7 +1944,11 @@ function cqn4sql(originalQuery, model) { const definition = prev?.elements?.[res] || getDefinition(prev?.target)?.elements[res] || pseudos.elements[res] const target = getParentEntity(definition) - thing.$refLinks[i] = { definition, target, alias: definition.name } + thing.$refLinks[i] = { + definition, + target, + alias: definition === assocRefLink.definition ? assocRefLink.alias : definition.name, + } return prev?.elements?.[res] || getDefinition(prev?.target)?.elements[res] || pseudos.elements[res] }, assocHost) } @@ -1959,15 +1973,16 @@ function cqn4sql(originalQuery, model) { lhs.ref[0] in { $self: true, $projection: true } ? getParentEntity(assocRefLink.definition) : target, ) else { - const lhsLeafArt = lhs.ref && lhs.$refLinks.at(-1).definition - const rhsLeafArt = rhs.ref && rhs.$refLinks.at(-1).definition + const lhsLeafDef = lhs.ref && lhs.$refLinks.at(-1).definition + const rhsLeafDef = rhs.ref && rhs.$refLinks.at(-1).definition + const lhsFirstDef = lhs.$refLinks.find(r => r.definition.kind === 'element')?.definition // compare structures in on-condition - if ((lhsLeafArt?.target && rhsLeafArt?.target) || (lhsLeafArt?.elements && rhsLeafArt?.elements)) { + if ((lhsLeafDef?.target && rhsLeafDef?.target) || (lhsLeafDef?.elements && rhsLeafDef?.elements)) { if (rhs.$refLinks[0].definition !== assocRefLink.definition) { rhs.ref.unshift(targetSideRefLink.alias) rhs.$refLinks.unshift(targetSideRefLink) } - if (lhs.$refLinks[0].definition !== assocRefLink.definition) { + if (lhsFirstDef !== assocRefLink.definition) { lhs.ref.unshift(targetSideRefLink.alias) lhs.$refLinks.unshift(targetSideRefLink) } @@ -1977,16 +1992,15 @@ function cqn4sql(originalQuery, model) { i += res.length continue } - // naive assumption: if first step is the association itself, all following ref steps must be resolvable + // assumption: if first step is the association itself, all following ref steps must be resolvable // within target `assoc.assoc.fk` -> `assoc.assoc_fk` else if ( - lhs.$refLinks[0]?.definition === - getParentEntity(assocRefLink.definition).elements[assocRefLink.definition.name] + lhsFirstDef === getParentEntity(assocRefLink.definition).elements[assocRefLink.definition.name] ) - result[i].ref = [assocRefLink.alias, lhs.ref.slice(1).join('_')] + result[i].ref = [assocRefLink.alias, lhs.ref.slice(lhs.ref[0] === '$self' ? 2 : 1).join('_')] // naive assumption: if the path starts with an association which is not the association from // which the on-condition originates, it must be a foreign key and hence resolvable in the source - else if (lhs.$refLinks[0]?.definition.target) result[i].ref = [result[i].ref.join('_')] + else if (lhsFirstDef?.target) result[i].ref = [result[i].ref.join('_')] } } if (backlink) { @@ -2009,7 +2023,7 @@ function cqn4sql(originalQuery, model) { // sanity check: error out if we can't produce a join if (backlink.keys.length === 0) { throw new Error( - `Path step “${assocRefLink.alias}” is a self comparison with “${getFullName(backlink)}” that has no foreign keys`, + `Path step “${assocRefLink.definition.name}” is a self comparison with “${getFullName(backlink)}” that has no foreign keys`, ) } // managed backlink -> calculate fk-pk pairs @@ -2307,7 +2321,7 @@ function cqn4sql(originalQuery, model) { if ( inferred.SELECT?.from.uniqueSubqueryAlias && !inferred.SELECT?.from.as && - firstStep === getLastStringSegment(transformedQuery.SELECT.from.ref[0]) + getImplicitAlias(firstStep) === getImplicitAlias(transformedQuery.SELECT.from.ref[0]) ) { return inferred.SELECT?.from.uniqueSubqueryAlias } @@ -2316,7 +2330,7 @@ function cqn4sql(originalQuery, model) { } function getCombinedElementAlias(node) { - return getLastStringSegment(inferred.$combinedElements[node.ref[0].id || node.ref[0]]?.[0].index) + return inferred.$combinedElements[node.ref[0].id || node.ref[0]]?.[0].index } } function getTransformedFunctionArgs(args, $baseLink = null) { @@ -2389,17 +2403,6 @@ function hasLogicalOr(tokenStream) { return tokenStream.some(t => t in { OR: true, or: true }) } -/** - * Returns the last segment of a string after the last dot. - * - * @param {string} str - The input string. - * @returns {string} The last segment of the string after the last dot. If there is no dot in the string, the function returns the original string. - */ -function getLastStringSegment(str) { - const index = str.lastIndexOf('.') - return index != -1 ? str.substring(index + 1) : str -} - function getParentEntity(element) { if (element.kind === 'entity') return element else return getParentEntity(element.parent) diff --git a/db-service/lib/infer/index.js b/db-service/lib/infer/index.js index 9441c2211..5f136fea5 100644 --- a/db-service/lib/infer/index.js +++ b/db-service/lib/infer/index.js @@ -4,7 +4,7 @@ const cds = require('@sap/cds') const JoinTree = require('./join-tree') const { pseudos } = require('./pseudos') -const { isCalculatedOnRead } = require('../utils') +const { isCalculatedOnRead, getImplicitAlias } = require('../utils') const cdsTypes = cds.linked({ definitions: { Timestamp: { type: 'cds.Timestamp' }, @@ -92,7 +92,7 @@ function infer(originalQuery, model) { * Each key is a query source alias, and its value is the corresponding CSN Definition. * @returns {object} The updated `querySources` object with inferred sources from the `from` clause. */ - function inferTarget(from, querySources) { + function inferTarget(from, querySources, useTechnicalAlias = true) { const { ref } = from if (ref) { const { id, args } = ref[0] @@ -114,14 +114,14 @@ function infer(originalQuery, model) { from.uniqueSubqueryAlias || from.as || (ref.length === 1 - ? first.substring(first.lastIndexOf('.') + 1) - : (ref.at(-1).id || ref.at(-1))); + ? getImplicitAlias(first, useTechnicalAlias) + : getImplicitAlias(ref.at(-1).id || ref.at(-1), useTechnicalAlias)); if (alias in querySources) throw new Error(`Duplicate alias "${alias}"`) querySources[alias] = { definition: target, args } const last = from.$refLinks.at(-1) last.alias = alias } else if (from.args) { - from.args.forEach(a => inferTarget(a, querySources)) + from.args.forEach(a => inferTarget(a, querySources, false)) } else if (from.SELECT) { const subqueryInFrom = infer(from, model) // we need the .elements in the sources // if no explicit alias is provided, we make up one @@ -131,7 +131,7 @@ function infer(originalQuery, model) { } else if (typeof from === 'string') { // TODO: Create unique alias, what about duplicates? const definition = getDefinition(from) || cds.error`"${from}" not found in the definitions of your model` - querySources[from.substring(from.lastIndexOf('.') + 1)] = { definition } + querySources[getImplicitAlias(from, useTechnicalAlias)] = { definition } } else if (from.SET) { infer(from, model) } diff --git a/db-service/lib/utils.js b/db-service/lib/utils.js index e4914679b..2a5ebb223 100644 --- a/db-service/lib/utils.js +++ b/db-service/lib/utils.js @@ -38,9 +38,34 @@ function isCalculatedElement(def) { return def?.value } +/** + * Calculates the implicit table alias for a given string. + * + * Based on the last part of the string, the implicit alias is calculated + * by taking the first character and prepending it with '$'. + * A leading '$' is removed if the last part already starts with '$'. + * + * @example + * getImplicitAlias('Books') => '$B' + * getImplicitAlias('bookshop.Books') => '$B' + * getImplicitAlias('bookshop.$B') => '$B' + * + * @param {string} str - The input string. + * @returns {string} + */ +function getImplicitAlias(str, useTechnicalAlias = true) { + const index = str.lastIndexOf('.') + if(useTechnicalAlias) { + const postfix = (index != -1 ? str.substring(index + 1) : str).replace(/^\$/, '')[0] || /* str === '$' */ '$' + return '$' + postfix + } + return index != -1 ? str.substring(index + 1) : str +} + // export the function to be used in other modules module.exports = { prettyPrintRef, isCalculatedOnRead, - isCalculatedElement + isCalculatedElement, + getImplicitAlias, } diff --git a/db-service/test/bookshop/db/schema.cds b/db-service/test/bookshop/db/schema.cds index 686e2a638..bc30e4666 100644 --- a/db-service/test/bookshop/db/schema.cds +++ b/db-service/test/bookshop/db/schema.cds @@ -421,7 +421,7 @@ entity Unmanaged { entity Item { key ID: Integer; - item: Association to Item; + Item: Association to Item; } entity Posts { @@ -430,3 +430,15 @@ entity Posts { iSimilar: Association to many Posts on UPPER(name) = UPPER(iSimilar.name); iSimilarNested: Association to many Posts on UPPER(iSimilarNested.name) = UPPER(LOWER(UPPER(name)), name); } + +entity ![$special] { + key ID: Integer; + name: String; + ![$special] : Association to ![$special]; +} + +entity ![$] { + key ID: Integer; + name: String; + ![$] : Association to ![$]; +} diff --git a/db-service/test/cds-infer/column.element.test.js b/db-service/test/cds-infer/column.element.test.js index c45829cc9..2a234bc96 100644 --- a/db-service/test/cds-infer/column.element.test.js +++ b/db-service/test/cds-infer/column.element.test.js @@ -90,7 +90,7 @@ describe('assign element onto columns', () => { describe('scoped queries', () => { it('use table alias of scoped query (assoc defined via type reference)', () => { - let inferred = _inferred(cds.ql`SELECT from bookshop.Books:coAuthor { + let inferred = _inferred(cds.ql`SELECT from bookshop.Books:coAuthor as coAuthor { coAuthor.name as name }`) let { Authors } = model.entities diff --git a/db-service/test/cds-infer/elements.test.js b/db-service/test/cds-infer/elements.test.js index 48b0e098b..142bdcfb1 100644 --- a/db-service/test/cds-infer/elements.test.js +++ b/db-service/test/cds-infer/elements.test.js @@ -26,7 +26,7 @@ describe('infer elements', () => { }) it('along multiple associations', () => { - let query = cds.ql`SELECT from bookshop.Books { ID, Books.genre.parent.ID }` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, Books.genre.parent.ID }` let inferred = _inferred(query) let { Books } = model.entities expect(inferred.elements).to.deep.equal({ @@ -118,7 +118,7 @@ describe('infer elements', () => { describe('access elements via table alias', () => { it('implicit alias via entity name', () => { - let query = cds.ql`SELECT from bookshop.Books { ID, Books.author }` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, Books.author }` let inferred = _inferred(query) let { Books } = model.entities expect(inferred.elements).to.deep.equal({ @@ -252,7 +252,7 @@ describe('infer elements', () => { }) describe('scoped queries', () => { it('use table alias of scoped query', () => { - let inferred = _inferred(cds.ql`SELECT from bookshop.Books:genre.foo { + let inferred = _inferred(cds.ql`SELECT from bookshop.Books:genre.foo as foo { foo.ID as fooID }`) let { Books, Genres } = model.entities @@ -267,7 +267,7 @@ describe('infer elements', () => { }) it('use table alias of scoped query (assoc defined via type reference)', () => { - let inferred = _inferred(cds.ql`SELECT from bookshop.Books:coAuthor { + let inferred = _inferred(cds.ql`SELECT from bookshop.Books:coAuthor as coAuthor { coAuthor.name as name }`) let { Books, Authors } = model.entities @@ -281,7 +281,7 @@ describe('infer elements', () => { describe('subqueries', () => { it('supports expressions and subqueries in the select list', () => { let query = cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { 1 + 1 as Two, (select from (select from bookshop.Authors) as A) as subquery }` @@ -299,7 +299,7 @@ describe('infer elements', () => { describe('expressions', () => { it('supports expressions and subqueries in the select list', () => { let query = cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { 1 + 1 as Two, (select from (select from bookshop.Authors) as A) as subquery }` @@ -322,7 +322,7 @@ describe('infer elements', () => { it('infers functions results as query element', () => { let query = cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { func(stock*price) as net, }` let inferred = _inferred(query) @@ -336,7 +336,7 @@ describe('infer elements', () => { }) it('supports an expression with fields in the select list', () => { - let query = cds.ql`SELECT from bookshop.Books { title + descr as noType }` + let query = cds.ql`SELECT from bookshop.Books as Books { title + descr as noType }` let inferred = _inferred(query) let { Books } = model.entities @@ -350,7 +350,7 @@ describe('infer elements', () => { describe('casts', () => { it('simple values, cdl style cast', () => { - let query = CQL(`SELECT from bookshop.Books { + let query = CQL(`SELECT from bookshop.Books as Books { 5 as price, 3.14 as pi, 3.1415 as pid : cds.Decimal(5,4), 'simple string' as string, @@ -412,7 +412,7 @@ describe('infer elements', () => { }) it('supports a cast expression in the select list', () => { - let query = cds.ql`SELECT from bookshop.Books { cast(cast(ID as Integer) as String) as IDS, cast(ID as bookshop.DerivedFromDerivedString) as IDCustomType }` + let query = cds.ql`SELECT from bookshop.Books as Books { cast(cast(ID as Integer) as String) as IDS, cast(ID as bookshop.DerivedFromDerivedString) as IDCustomType }` let inferred = _inferred(query) let { Books } = model.entities expect(inferred.sources).to.have.nested.property('Books.definition', Books) @@ -453,7 +453,7 @@ describe('infer elements', () => { describe('wildcards', () => { it('* in the column list', () => { - let query = cds.ql`SELECT from bookshop.Books { * }` + let query = cds.ql`SELECT from bookshop.Books as Books { * }` let inferred = _inferred(query) let { Books } = model.entities @@ -496,7 +496,7 @@ describe('infer elements', () => { // some more excluding tests it('replaces a select item coming from wildcard if it is overridden', () => { - let query = cds.ql`SELECT from bookshop.Books { 5 * 5 as price, *, 1 + 1 as ID, author.name as author }` // TODO: take care of order + let query = cds.ql`SELECT from bookshop.Books as Books { 5 * 5 as price, *, 1 + 1 as ID, author.name as author }` // TODO: take care of order let inferred = _inferred(query) let { Books } = model.entities expect(inferred.sources).to.have.nested.property('Books.definition', Books) diff --git a/db-service/test/cds-infer/negative.test.js b/db-service/test/cds-infer/negative.test.js index b69c6d05a..65f15c830 100644 --- a/db-service/test/cds-infer/negative.test.js +++ b/db-service/test/cds-infer/negative.test.js @@ -95,7 +95,7 @@ describe('negative', () => { }) it('$self reference is not found in the query elements -> infer hints alternatives', () => { - let query = cds.ql`SELECT from bookshop.Books { ID, $self.author }` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, $self.author }` expect(() => _inferred(query)).to.throw( /"author" not found in the columns list of query, did you mean "Books.author"?/, // revisit: error message ) @@ -185,27 +185,27 @@ describe('negative', () => { // queries with multiple sources are not supported for cqn4sql transformation (at least for now) // however, such queries can still be inferred it('element reference is ambiguous', () => { - let query = cds.ql`SELECT from bookshop.Books, bookshop.Authors { ID }` + let query = cds.ql`SELECT from bookshop.Books as Books, bookshop.Authors as Authors { ID }` expect(() => _inferred(query)).to.throw(/ambiguous reference to "ID", write "Books.ID", "Authors.ID" instead/) }) it('table alias is ambiguous', () => { - let query = cds.ql`SELECT from bookshop.Books, bookshop.Books { * }` + let query = cds.ql`SELECT from bookshop.Books as Books, bookshop.Books as Books { * }` expect(() => _inferred(query)).to.throw(/Duplicate alias "Books"/) }) it('table alias via association is ambiguous', () => { - let query = cds.ql`SELECT from bookshop.Books:author join bookshop.Books:author on 1 = 1 { * }` + let query = cds.ql`SELECT from bookshop.Books:author as author join bookshop.Books:author as author on 1 = 1 { * }` expect(() => _inferred(query)).to.throw(/Duplicate alias "author"/) }) it('wildcard (no projection) is ambiguous', () => { - let query = cds.ql`SELECT from bookshop.Books, bookshop.Foo` + let query = cds.ql`SELECT from bookshop.Books as Books, bookshop.Foo as Foo` expect(() => _inferred(query)).to.throw(/select "ID" explicitly with "Books.ID", "Foo.ID"/) }) it('wildcard (*) is ambiguous', () => { - let query = cds.ql`SELECT from bookshop.Books, bookshop.Authors { * }` + let query = cds.ql`SELECT from bookshop.Books as Books, bookshop.Authors as Authors { * }` expect(() => _inferred(query)).to.throw( `Ambiguous wildcard elements: select "createdAt" explicitly with "Books.createdAt", "Authors.createdAt" @@ -217,7 +217,7 @@ describe('negative', () => { }) it('wildcard (*) is ambiguous with subqueries', () => { - let query = cds.ql`SELECT from (select from bookshop.Books) as BooksSub, bookshop.Authors { * }` + let query = cds.ql`SELECT from (select from bookshop.Books) as BooksSub, bookshop.Authors as Authors { * }` expect(() => _inferred(query)).to.throw( `Ambiguous wildcard elements: select "createdAt" explicitly with "BooksSub.createdAt", "Authors.createdAt" @@ -432,7 +432,7 @@ describe('negative', () => { describe('order by', () => { it('reject join relevant path via queries own columns', () => { - let query = cds.ql`SELECT from bookshop.Books { + let query = cds.ql`SELECT from bookshop.Books as Books { ID, author, coAuthor as co diff --git a/db-service/test/cds-infer/nested-projections.test.js b/db-service/test/cds-infer/nested-projections.test.js index 9fd81705d..b58effad0 100644 --- a/db-service/test/cds-infer/nested-projections.test.js +++ b/db-service/test/cds-infer/nested-projections.test.js @@ -178,7 +178,7 @@ describe('nested projections', () => { }) }) it('wildcard expand with explicit table alias', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { Books { *, 'overwrite ID' as ID } }` let { Books } = model.entities diff --git a/db-service/test/cds-infer/source.test.js b/db-service/test/cds-infer/source.test.js index e75fa16ed..2da12f68e 100644 --- a/db-service/test/cds-infer/source.test.js +++ b/db-service/test/cds-infer/source.test.js @@ -47,7 +47,7 @@ describe('scoped queries', () => { }) it('select from association', () => { - let query = cds.ql`SELECT from bookshop.Books:author { ID }` + let query = cds.ql`SELECT from bookshop.Books:author as author { ID }` let inferred = _inferred(query) let { Authors } = model.entities @@ -58,13 +58,13 @@ describe('scoped queries', () => { expect(Object.keys(inferred.elements)).to.have.lengthOf(query.SELECT.columns.length) }) it('navigate along multiple assocs', () => { - let query = cds.ql`SELECT from bookshop.Books:author.books` + let query = cds.ql`SELECT from bookshop.Books:author.books as books` let inferred = _inferred(query) let { Books } = model.entities expect(inferred.sources).to.have.nested.property('books.definition', Books) }) it('multiple assocs with filter', () => { - let query = cds.ql`SELECT from bookshop.Books[201]:author[111].books` + let query = cds.ql`SELECT from bookshop.Books[201]:author[111].books as books` let inferred = _inferred(query) let { Books } = model.entities expect(inferred.sources).to.have.nested.property('books.definition', Books) @@ -77,7 +77,7 @@ describe('subqueries', () => { }) it('subquery in from', () => { - let query = cds.ql`SELECT from (select from bookshop.Books { ID as barID }) as Bar { barID }` + let query = cds.ql`SELECT from (select from bookshop.Books as Books { ID as barID }) as Bar { barID }` let inferred = _inferred(query) let { Books } = model.entities @@ -89,7 +89,7 @@ describe('subqueries', () => { }) it('subquery in from with wildcard', () => { - let query = cds.ql`SELECT from (select from bookshop.Books) as Bar { ID, author }` + let query = cds.ql`SELECT from (select from bookshop.Books as Books) as Bar { ID, author }` let inferred = _inferred(query) let { Books } = model.entities @@ -127,7 +127,7 @@ describe('multiple sources', () => { it('infers multiple table aliases as the queries source with a nested join', () => { let inferred = _inferred(cds.ql` - SELECT from bookshop.Books:author as Authors join bookshop.Books on 1 = 1 join bookshop.Foo on 1 = 1 { + SELECT from bookshop.Books:author as Authors join bookshop.Books as Books on 1 = 1 join bookshop.Foo As Foo on 1 = 1 { Authors.ID as aID, Books.ID as bID, Foo.ID as fooID diff --git a/db-service/test/cqn4sql/API.test.js b/db-service/test/cqn4sql/API.test.js index 9ad1a714f..4e6630d0f 100644 --- a/db-service/test/cqn4sql/API.test.js +++ b/db-service/test/cqn4sql/API.test.js @@ -14,7 +14,7 @@ describe('Repetitive calls to cqn4sql must work', () => { }) it('query can be extended by another element', () => { - const original = cds.ql`SELECT from bookshop.Books { ID }` + const original = cds.ql`SELECT from bookshop.Books as Books { ID }` let query = cqn4sql(original, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID }`) original.SELECT.columns.push({ ref: ['title'] }) @@ -27,14 +27,14 @@ describe('Repetitive calls to cqn4sql must work', () => { SELECT from bookshop.Books as Books { Books.ID, Books.title } WHERE EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID + SELECT 1 from bookshop.Authors as $a where $a.ID = Books.author_ID ) `, ) }) it('accepts empty select list', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { }`) }) diff --git a/db-service/test/cqn4sql/DELETE.test.js b/db-service/test/cqn4sql/DELETE.test.js index 666617d16..3f1ade031 100644 --- a/db-service/test/cqn4sql/DELETE.test.js +++ b/db-service/test/cqn4sql/DELETE.test.js @@ -11,7 +11,7 @@ describe('DELETE', () => { }) it('flatten structured access in where', () => { const { DELETE } = cds.ql - let d = DELETE.from('bookshop.Books').where({ 'dedication.text': { '=': 'foo' } }) + let d = DELETE.from('bookshop.Books as Books').where({ 'dedication.text': { '=': 'foo' } }) const query = cqn4sql(d, model) const expected = JSON.parse( '{"DELETE":{"from":{"ref": ["bookshop.Books"], "as": "Books"},"where":[{"ref":["Books","dedication_text"]},"=",{"val":"foo"}]}}', @@ -21,7 +21,7 @@ describe('DELETE', () => { it('DELETE with where exists expansion', () => { const { DELETE } = cds.ql - let d = DELETE.from('bookshop.Books:author') + let d = DELETE.from('bookshop.Books:author as author') const query = cqn4sql(d, model) // how to express this in CQN? // DELETE.from({ref: ['bookshop.Authors'], as: 'author'}).where('exists ( SELECT 1 from bookshop.Books as Books where author_ID = author.ID)') @@ -41,7 +41,7 @@ describe('DELETE', () => { "ref": [ "bookshop.Books" ], - "as": "Books" + "as": "$B" }, "columns": [ { @@ -51,7 +51,7 @@ describe('DELETE', () => { "where": [ { "ref": [ - "Books", + "$B", "author_ID" ] }, @@ -73,7 +73,7 @@ describe('DELETE', () => { it('DELETE with where exists expansion and path expression', () => { const forNodeModel = cds.compile.for.nodejs(JSON.parse(JSON.stringify(cds.model))) const { DELETE } = cds.ql - let d = DELETE.from('bookshop.Books:author').where(`books.title = 'Harry Potter'`) + let d = DELETE.from('bookshop.Books:author as author').where(`books.title = 'Harry Potter'`) const query = cqn4sql(d, forNodeModel) // this is the final exists subquery @@ -81,7 +81,7 @@ describe('DELETE', () => { SELECT author.ID from bookshop.Authors as author left join bookshop.Books as books on books.author_ID = author.ID where exists ( - SELECT 1 from bookshop.Books as Books2 where Books2.author_ID = author.ID + SELECT 1 from bookshop.Books as $B where $B.author_ID = author.ID ) and books.title = 'Harry Potter' ` const expected = JSON.parse(`{ @@ -131,6 +131,7 @@ describe('DELETE', () => { }, 'author', ], + as: 'author' }, }, } @@ -147,7 +148,7 @@ describe('DELETE', () => { SELECT: { from: { ref: ['bookshop.Books'], - as: 'Books', + as: '$B', }, columns: [ { @@ -156,7 +157,7 @@ describe('DELETE', () => { ], where: [ { - ref: ['Books', 'author_ID'], + ref: ['$B', 'author_ID'], }, '=', { @@ -164,7 +165,7 @@ describe('DELETE', () => { }, 'and', { - ref: ['Books', 'ID'], + ref: ['$B', 'ID'], }, 'in', { @@ -186,7 +187,7 @@ describe('DELETE', () => { it('DELETE with assoc filter and where exists expansion', () => { const { DELETE } = cds.ql - let d = DELETE.from('bookshop.Reproduce[author = null and ID = 99]:accessGroup') + let d = DELETE.from('bookshop.Reproduce[author = null and ID = 99]:accessGroup as accessGroup') const query = cqn4sql(d, model) const expected = { @@ -201,7 +202,7 @@ describe('DELETE', () => { SELECT: { from: { ref: ['bookshop.Reproduce'], - as: 'Reproduce', + as: '$R', }, columns: [ { @@ -210,7 +211,7 @@ describe('DELETE', () => { ], where: [ { - ref: ['Reproduce', 'accessGroup_ID'], + ref: ['$R', 'accessGroup_ID'], }, '=', { @@ -220,7 +221,7 @@ describe('DELETE', () => { { xpr: [ { - ref: ['Reproduce', 'author_ID'], + ref: ['$R', 'author_ID'], }, '=', { @@ -230,7 +231,7 @@ describe('DELETE', () => { }, 'and', { - ref: ['Reproduce', 'ID'], + ref: ['$R', 'ID'], }, '=', { diff --git a/db-service/test/cqn4sql/UPDATE.test.js b/db-service/test/cqn4sql/UPDATE.test.js index 7caa011f1..f19fcc99c 100644 --- a/db-service/test/cqn4sql/UPDATE.test.js +++ b/db-service/test/cqn4sql/UPDATE.test.js @@ -14,7 +14,7 @@ describe('UPDATE', () => { // cqn4sql normalizes it to `entity: {"ref": […], "as": "…"}` it('normalize update target format', () => { const { UPDATE } = cds.ql - let u = UPDATE.entity('bookshop.Books').where({ 'dedication.text': { '=': 'foo' } }) + let u = UPDATE.entity('bookshop.Books as Books').where({ 'dedication.text': { '=': 'foo' } }) const query = cqn4sql(u, model) const expected = JSON.parse( '{"UPDATE":{"entity":{"ref":["bookshop.Books"], "as": "Books"},"where":[{"ref":["Books","dedication_text"]},"=",{"val":"foo"}]}}', @@ -30,14 +30,14 @@ describe('UPDATE', () => { }) it('xpr in UPDATE with "with" are be considered', () => { const { UPDATE } = cds.ql - let u = UPDATE.entity('bookshop.Books').with({ + let u = UPDATE.entity('bookshop.Books as Books').with({ applyDiscount: { func: 'discount', args: [{ ref: ['price'] }, { ref: ['dedication', 'sub', 'foo'] }], }, getAuthors: { SELECT: { - from: { ref: ['bookshop.Authors'] }, + from: { ref: ['bookshop.Authors'], as: 'Authors' }, columns: [ { ref: ['name'] }, { @@ -72,11 +72,11 @@ describe('UPDATE', () => { it('Update with path expressions in where is handled', () => { const { UPDATE } = cds.ql - let u = UPDATE.entity({ ref: ['bookshop.Books'] }).where( + let u = UPDATE.entity('bookshop.Books as Books').where( `author.name LIKE '%Bron%' or ( author.name LIKE '%King' and title = 'The Dark Tower') and stock >= 15`, ) - let expected = UPDATE.entity({ ref: ['bookshop.Books'] }) + let expected = UPDATE.entity('bookshop.Books as Books') expected.UPDATE.where = [ { list: [{ ref: ['Books2', 'ID'] }] }, @@ -98,9 +98,9 @@ describe('UPDATE', () => { it('Update with path expressions to many', () => { const { UPDATE } = cds.ql - let u = UPDATE.entity({ ref: ['bookshop.Authors'] }).where(`books.title LIKE '%Heights%'`) + let u = UPDATE.entity('bookshop.Authors as Authors').where(`books.title LIKE '%Heights%'`) - let expected = UPDATE.entity({ ref: ['bookshop.Authors'] }) + let expected = UPDATE.entity('bookshop.Authors as Authors') expected.UPDATE.where = [ { list: [{ ref: ['Authors2', 'ID'] }] }, @@ -123,7 +123,7 @@ describe('UPDATE', () => { // table alias in subquery should address Books instead of bookshop.Books it('UPDATE with where exists expansion', () => { const { UPDATE } = cds.ql - let u = UPDATE.entity('bookshop.Books').where('exists author') + let u = UPDATE.entity('bookshop.Books as Books').where('exists author') const query = cqn4sql(u, model) // console.log(JSON.stringify(query)) // how to express this in CQN? @@ -138,7 +138,7 @@ describe('UPDATE', () => { "ref": [ "bookshop.Authors" ], - "as": "author" + "as": "$a" }, "columns": [ { @@ -148,7 +148,7 @@ describe('UPDATE', () => { "where": [ { "ref": [ - "author", + "$a", "ID" ] }, @@ -178,7 +178,7 @@ describe('UPDATE with path expression', () => { it('with path expressions with draft enabled entity', () => { const { UPDATE } = cds.ql - let u = UPDATE.entity({ ref: ['bookshop.CatalogService.Books'] }).where(`author.name LIKE '%Bron%'`) + let u = UPDATE.entity('bookshop.CatalogService.Books as Books').where(`author.name LIKE '%Bron%'`) let expected = UPDATE.entity({ ref: ['bookshop.CatalogService.Books'] }) @@ -202,7 +202,7 @@ describe('UPDATE with path expression', () => { }) it('path expression via calculated element leads to subquery if used in where', () => { - const q = UPDATE('bookshop.Orders.Items').set({ price: 5 }).where('price = 4.99') + const q = UPDATE('bookshop.Orders.Items as Items').set({ price: 5 }).where('price = 4.99') const res = cqn4sql(q, model) @@ -227,7 +227,7 @@ describe('UPDATE with path expression', () => { }) it('if there is no path expression in the where, we dont need subselect magic', () => { - const q = UPDATE('bookshop.Orders.Items').set({ quantity: 3 }).where('1 = 1') + const q = UPDATE('bookshop.Orders.Items as Items').set({ quantity: 3 }).where('1 = 1') const res = cqn4sql(q, model) expect(JSON.parse(JSON.stringify(res))).to.eql({ UPDATE: { diff --git a/db-service/test/cqn4sql/assocs2joins.test.js b/db-service/test/cqn4sql/assocs2joins.test.js index 7a38fd02d..abdfa74d7 100644 --- a/db-service/test/cqn4sql/assocs2joins.test.js +++ b/db-service/test/cqn4sql/assocs2joins.test.js @@ -15,7 +15,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // (SMW) need to decide: which fields to put on lhs of ON and which on right? // this test assumes that the "target" fields come on lhs it('in select, one assoc, one field', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, author.name }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, author.name }`, model) const expected = cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.ID, author.name as author_name } @@ -24,7 +24,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('in select, one deep assoc, with filter', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, dedication.addressee[name = 'Hasso'].name }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, dedication.addressee[name = 'Hasso'].name }`, model) const expected = cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Person as addressee on addressee.ID = Books.dedication_addressee_ID and addressee.name = 'Hasso' { Books.ID, addressee.name as dedication_addressee_name } @@ -33,7 +33,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('in select, two assocs, second navigates to foreign key', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Authors { ID, books.genre.ID }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Authors as Authors { ID, books.genre.ID }`, model) const expected = cds.ql`SELECT from bookshop.Authors as Authors left outer join bookshop.Books as books on books.author_ID = Authors.ID { Authors.ID, books.genre_ID as books_genre_ID } @@ -41,7 +41,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { expect(query).to.deep.equal(expected) }) it('in select, two assocs, second shortcuts to foreign key', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Authors { ID, books.genre }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Authors as Authors { ID, books.genre }`, model) const expected = cds.ql`SELECT from bookshop.Authors as Authors left outer join bookshop.Books as books on books.author_ID = Authors.ID { Authors.ID, books.genre_ID as books_genre_ID } @@ -49,7 +49,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { expect(query).to.deep.equal(expected) }) it('in select, three assocs, last navigates to foreign key', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Authors { ID, books.genre.parent.ID as foo }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Authors as Authors { ID, books.genre.parent.ID as foo }`, model) const expected = cds.ql`SELECT from bookshop.Authors as Authors left outer join bookshop.Books as books on books.author_ID = Authors.ID left outer join bookshop.Genres as genre on genre.ID = books.genre_ID @@ -58,7 +58,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { expect(query).to.deep.equal(expected) }) it('in select, two assocs, last shortcuts to structured foreign key', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Intermediate { ID, toAssocWithStructuredKey.toStructuredKey }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Intermediate as Intermediate { ID, toAssocWithStructuredKey.toStructuredKey }`, model) const expected = cds.ql`SELECT from bookshop.Intermediate as Intermediate left outer join bookshop.AssocWithStructuredKey as toAssocWithStructuredKey on toAssocWithStructuredKey.ID = Intermediate.toAssocWithStructuredKey_ID { Intermediate.ID, @@ -70,7 +70,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { expect(query).to.deep.equal(expected) }) it('in select, one assoc, structured access', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.EStrucSibling { ID, self.struc1 }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.EStrucSibling as EStrucSibling { ID, self.struc1 }`, model) const expected = cds.ql`SELECT from bookshop.EStrucSibling as EStrucSibling left outer join bookshop.EStrucSibling as self on self.ID = EStrucSibling.self_ID { @@ -84,7 +84,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // same assoc used twice -> only one JOIN it('in select, two paths, same assoc', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, author.name, author.dateOfBirth }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, author.name, author.dateOfBirth }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.ID, author.name as author_name, author.dateOfBirth as author_dateOfBirth } @@ -93,7 +93,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // same assoc used twice -> only one JOIN it('in select, path with assoc.struct', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, author.name, author.address.street }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, author.name, author.address.street }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.ID, author.name as author_name, author.address_street as author_address_street } @@ -112,7 +112,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // assoc inside struc // -> to be clarified: what should be the table alias? it('in select, path with struct.assoc', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, dedication.addressee.name }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, dedication.addressee.name }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Person as addressee on addressee.ID = Books.dedication_addressee_ID { Books.ID, addressee.name as dedication_addressee_name } @@ -122,7 +122,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // order of select items should stay untouched, no matter what path they follow it('in select, path with two assocs', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors { + cds.ql`SELECT from bookshop.Authors as Authors { name, books.genre.descr, books.title as books_title, @@ -145,7 +145,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select, path with two assocs / respect explicit column alias', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors { + cds.ql`SELECT from bookshop.Authors as Authors { name, books.genre.descr as foo, books.title as books_title, @@ -168,7 +168,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select, unrelated paths', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, author.name, genre.descr, dedication.addressee.name, author.dateOfBirth }`, + cds.ql`SELECT from bookshop.Books as Books { ID, author.name, genre.descr, dedication.addressee.name, author.dateOfBirth }`, model, ) let expected = cds.ql`SELECT from bookshop.Books as Books @@ -186,7 +186,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // 2 different assocs lead to 2 JOINs, even if they have same target it('in select, different assocs with same target', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, author.name, coAuthor.name }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, author.name, coAuthor.name }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID left outer join bookshop.Authors as coAuthor on coAuthor.ID = Books.coAuthor_ID @@ -196,7 +196,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select, paths with common prefix path', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors { + cds.ql`SELECT from bookshop.Authors as Authors { name, books.genre.descr, books.coAuthor.name, books.genre.code, books.coAuthor.dateOfBirth }`, model, ) @@ -215,7 +215,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select, following recursive assoc', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { title, genre.descr, genre.parent.descr, @@ -259,7 +259,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('in select, follow managed assoc, select FK', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { author.ID }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { author.ID }`, model) const expected = cds.ql`SELECT from bookshop.Books as Books { Books.author_ID @@ -271,7 +271,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // TODO (SMW) decide: if we generate a join, should we then take the FK from source or from target? // currently we take it from the source it('in select, follow managed assoc, select FK and other field', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { author.ID, author.name }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { author.ID, author.name }`, model) const expected = cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { @@ -283,7 +283,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('in where, one assoc, one field', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } where author.name = 'Schiller'`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } where author.name = 'Schiller'`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.ID } WHERE author.name = 'Schiller' @@ -291,7 +291,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('in where, one assoc, one field (2)', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } where author.name like 'Schiller'`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } where author.name like 'Schiller'`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.ID } WHERE author.name like 'Schiller' @@ -299,7 +299,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('in where, one assoc in xpr, one field', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } where ((author.name + 's') = 'Schillers')`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } where ((author.name + 's') = 'Schillers')`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.ID } WHERE ((author.name + 's') = 'Schillers') @@ -308,7 +308,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in where, one assoc in multiple xpr, one field', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID } where ((author.name + 's') = 'Schillers') or ((author.name + 's') = 'Goethes')`, + cds.ql`SELECT from bookshop.Books as Books { ID } where ((author.name + 's') = 'Schillers') or ((author.name + 's') = 'Goethes')`, model, ) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books @@ -318,14 +318,14 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('list in where', () => { - let query = cds.ql`SELECT from bookshop.Books { ID } where (author.name, 1) in ('foo', 'bar')` + let query = cds.ql`SELECT from bookshop.Books as Books { ID } where (author.name, 1) in ('foo', 'bar')` let expected = cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.ID }where (author.name, 1) in ('foo', 'bar')` expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('tuple list in where', () => { - let query = cds.ql`SELECT from bookshop.Books { ID } where ((author.name, genre.name), 1) in (('foo', 1), ('bar', 2))` + let query = cds.ql`SELECT from bookshop.Books as Books { ID } where ((author.name, genre.name), 1) in (('foo', 1), ('bar', 2))` let expected = cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID left outer join bookshop.Genres as genre on genre.ID = Books.genre_ID @@ -333,7 +333,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('list in having', () => { - let query = cds.ql`SELECT from bookshop.Books { ID } having (author.name, 1) in ('foo', 'bar')` + let query = cds.ql`SELECT from bookshop.Books as Books { ID } having (author.name, 1) in ('foo', 'bar')` let expected = cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.ID } having (author.name, 1) in ('foo', 'bar')` @@ -342,7 +342,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select & where, same assoc', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, author.name } where author.placeOfBirth = 'Marbach'`, + cds.ql`SELECT from bookshop.Books as Books { ID, author.name } where author.placeOfBirth = 'Marbach'`, model, ) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books @@ -357,7 +357,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // filters are not part of the implicit alias generated for the result columns it('in select, assoc with filter', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, author[placeOfBirth='Marbach'].name }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, author[placeOfBirth='Marbach'].name }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID AND author.placeOfBirth = 'Marbach' { Books.ID, author.name as author_name } @@ -365,7 +365,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('in select, assoc with filter - no special handling for FK access in filter', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, author[ID=2].name }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, author[ID=2].name }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID AND author.ID = 2 { Books.ID, author.name as author_name } @@ -381,7 +381,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // TODO (SMW) new test it('in select, assoc with filter using OR', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, author[placeOfBirth='Marbach' OR placeOfDeath='Marbach'].name }`, + cds.ql`SELECT from bookshop.Books as Books { ID, author[placeOfBirth='Marbach' OR placeOfDeath='Marbach'].name }`, model, ) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books @@ -394,7 +394,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // TODO (SMW) new test // if FK field is accessed with filter, a JOIN is generated and the FK must be fetched from the association target it('in select, access to FK field with filter', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { title, author[name='Mr. X' or name = 'Mr. Y'].ID }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { title, author[name='Mr. X' or name = 'Mr. Y'].ID }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID AND (author.name='Mr. X' or author.name = 'Mr. Y') { Books.title, author.ID as author_ID } @@ -403,7 +403,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select + having, assoc with filter is always join relevant', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, author[placeOfBirth='Marbach'].ID as aID1, } having author[placeOfBirth='Foobach'].ID and genre[parent.ID='fiction'].ID`, @@ -425,7 +425,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // same assoc with and without filter -> 2 joins it('in select, assoc with and without filter', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, author[placeOfBirth='Marbach'].name as n1, author.name as n2 @@ -445,7 +445,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // same filter - same join it('in select, several assocs with filter', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books + cds.ql`SELECT from bookshop.Books as Books { ID, author[placeOfBirth='Marbach'].name as n1, author[placeOfBirth='Erfurt'].name as n2, @@ -469,7 +469,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // we compare filters based on AST it('in select, filters with reversed conditino are not treated as equal', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books + cds.ql`SELECT from bookshop.Books as Books { ID, author[placeOfBirth='Marbach'].name as n1, author['Marbach'=placeOfBirth].name as n2 @@ -488,7 +488,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select, two levels of assocs (1)', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors + cds.ql`SELECT from bookshop.Authors as Authors { ID, books[stock=1].genre[code='A'].descr as d1, books[stock=1].genre[code='A'].descr as d2 @@ -507,7 +507,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select, two levels of assocs (2)', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors + cds.ql`SELECT from bookshop.Authors as Authors { ID, books[stock=1].genre[code='A'].descr as d1, books[stock=1].genre[code='B'].descr as d2 @@ -527,7 +527,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select, two levels of assocs (3)', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors + cds.ql`SELECT from bookshop.Authors as Authors { ID, books[stock=1].genre[code='A'].descr as d1, books[stock=2].genre[code='A'].descr as d2 @@ -548,7 +548,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select/where, two levels of assocs', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors + cds.ql`SELECT from bookshop.Authors as Authors { ID, books[stock=1].genre[code='A'].descr } where books[stock=1].genre[code='B'].descr = 'foo'`, @@ -566,7 +566,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select, two levels of assocs, with case', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors + cds.ql`SELECT from bookshop.Authors as Authors { ID, case when ID<4 then books[stock=1].genre[code='A'].descr when ID>4 then books[stock=1].genre[code='B'].descr @@ -589,7 +589,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { // TODO (SMW) new test it('in select, two levels of assocs, with case and exists', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors + cds.ql`SELECT from bookshop.Authors as Authors { ID, case when exists books[price>10] then books[stock=1].genre[code='A'].descr when exists books[price>100] then books[stock=1].genre[code='B' or code='C'].descr @@ -602,9 +602,9 @@ describe('Unfolding Association Path Expressions to Joins', () => { left outer join bookshop.Genres as genre on genre.ID = books.genre_ID AND genre.code = 'A' left outer join bookshop.Genres as genre2 on genre2.ID = books.genre_ID AND (genre2.code = 'B' or genre2.code = 'C') { Authors.ID, - case when exists (select 1 from bookshop.Books as books2 where books2.author_ID = Authors.ID and books2.price > 10) + case when exists (select 1 from bookshop.Books as $b where $b.author_ID = Authors.ID and $b.price > 10) then genre.descr - when exists (select 1 from bookshop.Books as books3 where books3.author_ID = Authors.ID and books3.price > 100) + when exists (select 1 from bookshop.Books as $b2 where $b2.author_ID = Authors.ID and $b2.price > 100) then genre2.descr end as descr } @@ -613,7 +613,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select, filter with exists', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors + cds.ql`SELECT from bookshop.Authors as Authors { ID, books[exists genre[code='A']].title }`, @@ -621,7 +621,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { ) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors left outer join bookshop.Books as books on books.author_ID = Authors.ID AND - exists (select 1 from bookshop.Genres as genre where genre.ID = books.genre_ID and genre.code = 'A') + exists (select 1 from bookshop.Genres as $g where $g.ID = books.genre_ID and $g.code = 'A') { Authors.ID, books.title as books_title } @@ -630,7 +630,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select, filter (with OR needs bracelets) with exists ', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors + cds.ql`SELECT from bookshop.Authors as Authors { ID, books[exists genre[code='A' or code='B']].title }`, @@ -638,7 +638,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { ) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors left outer join bookshop.Books as books on books.author_ID = Authors.ID AND - exists (select 1 from bookshop.Genres as genre where genre.ID = books.genre_ID and (genre.code = 'A' or genre.code = 'B')) + exists (select 1 from bookshop.Genres as $g where $g.ID = books.genre_ID and ($g.code = 'A' or $g.code = 'B')) { Authors.ID, books.title as books_title } @@ -646,7 +646,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('in having, one assoc, one field', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } having author.name = 'Schiller'`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } having author.name = 'Schiller'`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.ID } having author.name = 'Schiller' @@ -655,7 +655,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('in select & having, same assoc', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, author.name } having author.placeOfBirth = 'Marbach'`, + cds.ql`SELECT from bookshop.Books as Books { ID, author.name } having author.placeOfBirth = 'Marbach'`, model, ) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books @@ -665,7 +665,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('in select & having, same assoc with same filter -> only one join', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, author[placeOfBirth='Marbach'].name } having author[placeOfBirth='Marbach'].name = 'King'`, + cds.ql`SELECT from bookshop.Books as Books { ID, author[placeOfBirth='Marbach'].name } having author[placeOfBirth='Marbach'].name = 'King'`, model, ) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books @@ -675,14 +675,14 @@ describe('Unfolding Association Path Expressions to Joins', () => { }) it('in group by, one assoc, one field', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } group by author.name`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } group by author.name`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.ID } group by author.name `) }) it('in order by, one assoc, one field', () => { - const input = cds.ql`SELECT from bookshop.Books { ID } order by author.name asc` + const input = cds.ql`SELECT from bookshop.Books as Books { ID } order by author.name asc` let query = cqn4sql(input, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID @@ -690,7 +690,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { `) }) it('in order by, via wildcard', () => { - const input = cds.ql`SELECT from bookshop.Books.twin order by author.name asc` + const input = cds.ql`SELECT from bookshop.Books.twin as twin order by author.name asc` let query = cqn4sql(input, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books.twin as twin left outer join bookshop.Authors as author on author.ID = twin.author_ID @@ -698,7 +698,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { `) }) it('in group by, one assoc, wildcard select', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books group by author.name`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books group by author.name`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { @@ -728,7 +728,7 @@ describe('Unfolding Association Path Expressions to Joins', () => { it('properly rewrite association chains if intermediate assoc is not fk', () => { // this issue came up for ref: [genre.parent.ID] because "ID" is fk of "parent" // but "parent" is not fk of "genre" - const q = cds.ql`SELECT from (select genre, ID from bookshop.Books) as book { + const q = cds.ql`SELECT from (select genre, ID from bookshop.Books as Books) as book { ID } group by genre.parent.ID, genre.parent.name` const qx = cds.ql` @@ -753,7 +753,7 @@ describe('Variations on ON', () => { }) it('unmanaged 1', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, coAuthorUnmanaged.name }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, coAuthorUnmanaged.name }`, model) const expected = cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as coAuthorUnmanaged on coAuthorUnmanaged.ID = Books.coAuthor_ID_unmanaged @@ -763,7 +763,7 @@ describe('Variations on ON', () => { }) it('unmanaged 2', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Baz { id, parent.id as pid }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Baz as Baz { id, parent.id as pid }`, model) const expected = cds.ql`SELECT from bookshop.Baz as Baz left outer join bookshop.Baz as parent on parent.id = Baz.parent_id or parent.id > 17 @@ -774,7 +774,7 @@ describe('Variations on ON', () => { // TODO (SMW) original ON condition must be enclosed in parens if there is a filter it('unmanaged 2 plus filter', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Baz { id, parent[id < 19].id as pid }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Baz as Baz { id, parent[id < 19].id as pid }`, model) const expected = cds.ql`SELECT from bookshop.Baz as Baz left outer join bookshop.Baz as parent on (parent.id = Baz.parent_id or parent.id > 17) and parent.id < 19 @@ -810,7 +810,7 @@ describe('Variations on ON', () => { }) it('unmanaged assoc with on condition with length === 1', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions { ID, onlyOneRef.foo }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions as BooksWithWeirdOnConditions { ID, onlyOneRef.foo }`, model) const expected = cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions as BooksWithWeirdOnConditions left outer join bookshop.BooksWithWeirdOnConditions as onlyOneRef on BooksWithWeirdOnConditions.ID { BooksWithWeirdOnConditions.ID, onlyOneRef.foo as onlyOneRef_foo } @@ -818,7 +818,7 @@ describe('Variations on ON', () => { expect(query).to.deep.equal(expected) }) it('unmanaged assoc with on condition with odd length', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions { ID, oddNumber.foo }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions as BooksWithWeirdOnConditions { ID, oddNumber.foo }`, model) const expected = cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions as BooksWithWeirdOnConditions left outer join bookshop.BooksWithWeirdOnConditions as oddNumber on BooksWithWeirdOnConditions.foo / 5 + BooksWithWeirdOnConditions.ID = BooksWithWeirdOnConditions.ID + BooksWithWeirdOnConditions.foo { BooksWithWeirdOnConditions.ID, oddNumber.foo as oddNumber_foo } @@ -827,7 +827,7 @@ describe('Variations on ON', () => { }) it('unmanaged assoc with on condition accessing structured foreign keys', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions { ID, oddNumberWithForeignKeyAccess.second }`, + cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions as BooksWithWeirdOnConditions { ID, oddNumberWithForeignKeyAccess.second }`, model, ) const expected = cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions as BooksWithWeirdOnConditions @@ -838,7 +838,7 @@ describe('Variations on ON', () => { }) it('unmanaged assoc with on condition comparing to val', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions { ID, refComparedToVal.refComparedToValFlipped.foo }`, + cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions as BooksWithWeirdOnConditions { ID, refComparedToVal.refComparedToValFlipped.foo }`, model, ) const expected = cds.ql`SELECT from bookshop.BooksWithWeirdOnConditions as BooksWithWeirdOnConditions @@ -850,7 +850,7 @@ describe('Variations on ON', () => { }) it('accessing partial key after association implies join if not part of explicit FK', () => { - const original = cds.ql`SELECT from bookshop.PartialStructuredKey { toSelf.struct.one, toSelf.struct.two }` + const original = cds.ql`SELECT from bookshop.PartialStructuredKey as PartialStructuredKey { toSelf.struct.one, toSelf.struct.two }` const transformed = cqn4sql(original, model) const expected = cds.ql`SELECT from bookshop.PartialStructuredKey as PartialStructuredKey left outer join bookshop.PartialStructuredKey as toSelf on toSelf.struct_one = PartialStructuredKey.toSelf_partial @@ -873,7 +873,7 @@ describe('subqueries in from', () => { it('in select, use one assoc in FROM subquery', () => { let query = cqn4sql( - cds.ql`SELECT from (SELECT from bookshop.Books { author.name as author_name }) as Bar { Bar.author_name }`, + cds.ql`SELECT from (SELECT from bookshop.Books as Books { author.name as author_name }) as Bar { Bar.author_name }`, model, ) const expected = cds.ql`SELECT from ( @@ -886,7 +886,7 @@ describe('subqueries in from', () => { }) it('expose managed assoc in FROM subquery, expose in main select', () => { - let query = cqn4sql(cds.ql`SELECT from (SELECT from bookshop.Books { author }) as Bar { Bar.author }`, model) + let query = cqn4sql(cds.ql`SELECT from (SELECT from bookshop.Books as Books { author }) as Bar { Bar.author }`, model) const expected = cds.ql`SELECT from ( SELECT from bookshop.Books as Books { Books.author_ID } ) as Bar { Bar.author_ID } @@ -895,7 +895,7 @@ describe('subqueries in from', () => { }) it('expose managed assoc in FROM subquery with alias, expose in main select', () => { - let query = cqn4sql(cds.ql`SELECT from (SELECT from bookshop.Books { author as a }) as Bar { Bar.a }`, model) + let query = cqn4sql(cds.ql`SELECT from (SELECT from bookshop.Books as Books { author as a }) as Bar { Bar.a }`, model) const expected = cds.ql`SELECT from ( SELECT from bookshop.Books as Books { Books.author_ID as a_ID } ) as Bar { Bar.a_ID } @@ -907,7 +907,7 @@ describe('subqueries in from', () => { // the JOIN happens in the main query. it('expose managed assoc in FROM subquery, use in main select', () => { let query = cqn4sql( - cds.ql`SELECT from (SELECT from bookshop.Books { author }) as Bar + cds.ql`SELECT from (SELECT from bookshop.Books as Books { author }) as Bar { Bar.author.name }`, model, ) @@ -927,7 +927,7 @@ describe('subqueries in from', () => { model, ) const expected = cds.ql`SELECT from ( - SELECT from bookshop.Books as Books { Books.author_ID as a_ID } + SELECT from bookshop.Books as $B { $B.author_ID as a_ID } ) as Bar left outer join bookshop.Authors as a on a.ID = Bar.a_ID { a.name as a_name } @@ -943,9 +943,9 @@ describe('subqueries in from', () => { model, ) const expected = cds.ql`SELECT from ( - 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 } + SELECT from bookshop.Books as $B + left outer join bookshop.Authors as author on author.ID = $B.author_ID + { $B.author_ID, $B.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 books on books.author_ID = a.ID @@ -958,9 +958,9 @@ describe('subqueries in from', () => { // TODO move to extra section? it('assoc path in value subquery', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { title, - (select from bookshop.Genres { parent.code } where Genres.ID = Books.genre.ID) as pc + (select from bookshop.Genres as Genres { parent.code } where Genres.ID = Books.genre.ID) as pc }`, model, ) @@ -983,7 +983,7 @@ describe('Backlink Associations', () => { }) it('self managed', () => { let query = cqn4sql( - cds.ql`select from a2j.Header { + cds.ql`select from a2j.Header as Header { toItem_selfMgd.id, }`, model, @@ -996,7 +996,7 @@ describe('Backlink Associations', () => { it('self unmanaged', () => { let query = cqn4sql( - cds.ql`select from a2j.Header { + cds.ql`select from a2j.Header as Header { toItem_selfUmgd.id, }`, model, @@ -1010,7 +1010,7 @@ describe('Backlink Associations', () => { it('self combined', () => { let query = cqn4sql( - cds.ql`select from a2j.Header { + cds.ql`select from a2j.Header as Header { toItem_combined.id, }`, model, @@ -1029,7 +1029,7 @@ describe('Backlink Associations', () => { it('forward', () => { let query = cqn4sql( - cds.ql`select from a2j.Header { + cds.ql`select from a2j.Header as Header { toItem_fwd.id, }`, model, @@ -1043,7 +1043,7 @@ describe('Backlink Associations', () => { it('all of the above combined', () => { let query = cqn4sql( - cds.ql`select from a2j.Header { + cds.ql`select from a2j.Header as Header { toItem_selfMgd.id as selfMgd_id, toItem_selfUmgd.id as selfUmgd_id, toItem_combined.id as combined_id, @@ -1072,7 +1072,7 @@ describe('Backlink Associations', () => { it('backlink usage', () => { let query = cqn4sql( - cds.ql`select from a2j.Folder { + cds.ql`select from a2j.Folder as Folder { nodeCompanyCode.assignments.data }`, model, @@ -1091,7 +1091,7 @@ describe('Backlink Associations', () => { // compiler generates '$user.id' // cqn4sql generates `ref: ['$user', 'id']` it('Backlinks with other items in same on-condition', () => { let query = cqn4sql( - cds.ql`select from a2j.F { + cds.ql`select from a2j.F as F { toE.data }`, model, @@ -1115,7 +1115,7 @@ describe('Shared foreign key identity', () => { }) it('identifies FKs following toB', () => { let query = cqn4sql( - cds.ql`select from A { + cds.ql`select from A as A { a.b.c.toB.b.c.d.parent.c.d.e.ID as a_b_c_toB_foo_boo, a.b.c.toB.e.f.g.child.c.d.e.ID as a_b_c_toB_bar_bas }`, @@ -1138,7 +1138,7 @@ describe('Where exists in combination with assoc to join', () => { it('one assoc + one where exists / aliases are treated case insensitive', () => { let query = cqn4sql( - cds.ql`select from bookshop.Books:author { + cds.ql`select from bookshop.Books:author as author { books.genre.name, }`, model, @@ -1147,13 +1147,13 @@ describe('Where exists in combination with assoc to join', () => { left outer join bookshop.Books as books on books.author_ID = author.ID left outer join bookshop.Genres as genre on genre.ID = books.genre_ID { genre.name as books_genre_name } where exists ( - SELECT 1 from bookshop.Books as Books2 where Books2.author_ID = author.ID + SELECT 1 from bookshop.Books as $B where $B.author_ID = author.ID ) `) }) it('aliases for recursive assoc in column + recursive assoc in from must not clash', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors:books.genre.parent.parent.parent + cds.ql`SELECT from bookshop.Authors:books.genre.parent.parent.parent as parent { parent.parent.parent.descr, }`, model, ) @@ -1164,11 +1164,11 @@ describe('Where exists in combination with assoc to join', () => { parent3.descr as parent_parent_descr, } WHERE EXISTS ( - SELECT 1 from bookshop.Genres as parent4 where parent4.parent_ID = parent.ID and EXISTS ( - SELECT 1 from bookshop.Genres as parent5 where parent5.parent_ID = parent4.ID and EXISTS ( - SELECT 1 from bookshop.Genres as genre where genre.parent_ID = parent5.ID and EXISTS ( - SELECT 1 from bookshop.Books as books where books.genre_ID = genre.ID and EXISTS ( - SELECT 1 from bookshop.Authors as Authors where Authors.ID = books.author_ID + SELECT 1 from bookshop.Genres as $p where $p.parent_ID = parent.ID and EXISTS ( + SELECT 1 from bookshop.Genres as $p2 where $p2.parent_ID = $p.ID and EXISTS ( + SELECT 1 from bookshop.Genres as $g where $g.parent_ID = $p2.ID and EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.genre_ID = $g.ID and EXISTS ( + SELECT 1 from bookshop.Authors as $A where $A.ID = $b.author_ID ) ) ) @@ -1179,7 +1179,7 @@ describe('Where exists in combination with assoc to join', () => { // Revisit: Alias count order in where + from could be flipped it('aliases for recursive assoc in column + recursive assoc in from + where exists must not clash', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors:books.genre.parent.parent.parent + cds.ql`SELECT from bookshop.Authors:books.genre.parent.parent.parent as parent { parent.parent.parent.descr } where exists parent`, model, ) @@ -1190,17 +1190,17 @@ describe('Where exists in combination with assoc to join', () => { parent3.descr as parent_parent_descr, } WHERE EXISTS ( - SELECT 1 from bookshop.Genres as parent5 where parent5.parent_ID = parent.ID and EXISTS ( - SELECT 1 from bookshop.Genres as parent6 where parent6.parent_ID = parent5.ID and EXISTS ( - SELECT 1 from bookshop.Genres as genre where genre.parent_ID = parent6.ID and EXISTS ( - SELECT 1 from bookshop.Books as books where books.genre_ID = genre.ID and EXISTS ( - SELECT 1 from bookshop.Authors as Authors where Authors.ID = books.author_ID + SELECT 1 from bookshop.Genres as $p2 where $p2.parent_ID = parent.ID and EXISTS ( + SELECT 1 from bookshop.Genres as $p3 where $p3.parent_ID = $p2.ID and EXISTS ( + SELECT 1 from bookshop.Genres as $g where $g.parent_ID = $p3.ID and EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.genre_ID = $g.ID and EXISTS ( + SELECT 1 from bookshop.Authors as $A where $A.ID = $b.author_ID ) ) ) ) ) and EXISTS ( - SELECT 1 from bookshop.Genres as parent4 where parent4.ID = parent.parent_ID + SELECT 1 from bookshop.Genres as $p where $p.ID = parent.parent_ID )`) }) }) @@ -1212,7 +1212,7 @@ describe('comparisons of associations in on condition of elements needs to be ex }) it('assoc comparison needs to be expanded in on condition calculation', () => { - const query = cqn4sql(cds.ql`SELECT from a2j.Foo { ID, buz.foo }`, model) + const query = cqn4sql(cds.ql`SELECT from a2j.Foo as Foo { ID, buz.foo }`, model) const expected = cds.ql` SELECT from a2j.Foo as Foo left join a2j.Buz as buz on ((buz.bar_ID = Foo.bar_ID AND buz.bar_foo_ID = Foo.bar_foo_ID) and buz.foo_ID = Foo.ID){ Foo.ID, @@ -1221,7 +1221,7 @@ describe('comparisons of associations in on condition of elements needs to be ex expect(query).to.eql(expected) }) it('unmanaged association path traversal in on condition needs to be flattened', () => { - const query = cqn4sql(cds.ql`SELECT from a2j.Foo { ID, buzUnmanaged.foo }`, model) + const query = cqn4sql(cds.ql`SELECT from a2j.Foo as Foo { ID, buzUnmanaged.foo }`, model) const expected = cds.ql` SELECT from a2j.Foo as Foo left join a2j.Buz as buzUnmanaged on buzUnmanaged.bar_foo_ID = Foo.bar_foo_ID and buzUnmanaged.bar_ID = Foo.bar_ID and buzUnmanaged.foo_ID = Foo.ID @@ -1239,7 +1239,7 @@ describe('optimize fk access', () => { model = cds.model = await cds.load(__dirname + '/A2J/classes').then(cds.linked) }) it('association (with multiple, structured, renamed fks) is key', () => { - const query = cds.ql`SELECT from ForeignKeyIsAssoc { + const query = cds.ql`SELECT from ForeignKeyIsAssoc as ForeignKeyIsAssoc { my.room as teachersRoom, }` const expected = cds.ql`SELECT from ForeignKeyIsAssoc as ForeignKeyIsAssoc { @@ -1251,7 +1251,7 @@ describe('optimize fk access', () => { expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('association as key leads to non-key field', () => { - const query = cds.ql`SELECT from Pupils { + const query = cds.ql`SELECT from Pupils as Pupils { ID } group by classrooms.classroom.ID, classrooms.classroom.name` const expected = cds.ql`SELECT from Pupils as Pupils @@ -1266,7 +1266,7 @@ describe('optimize fk access', () => { expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('association as key leads to nested non-key field', () => { - const query = cds.ql`SELECT from Pupils { + const query = cds.ql`SELECT from Pupils as Pupils { ID } group by classrooms.classroom.ID, classrooms.classroom.info.capacity` const expected = cds.ql`SELECT from Pupils as Pupils @@ -1281,7 +1281,7 @@ describe('optimize fk access', () => { expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('two step path ends in foreign key simple ref', () => { - const query = cds.ql`SELECT from Classrooms { + const query = cds.ql`SELECT from Classrooms as Classrooms { pupils.pupil.ID as studentCount, } where Classrooms.ID = 1` const expected = cds.ql`SELECT from Classrooms as Classrooms left join ClassroomsPupils as pupils @@ -1292,7 +1292,7 @@ describe('optimize fk access', () => { expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('filters are always join relevant', () => { - const query = cds.ql`SELECT from ClassroomsPupils { + const query = cds.ql`SELECT from ClassroomsPupils as ClassroomsPupils { pupil[ID = 5].ID as student, }` const expected = cds.ql`SELECT from ClassroomsPupils as ClassroomsPupils @@ -1305,7 +1305,7 @@ describe('optimize fk access', () => { expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('optimized next to non-optimized', () => { - const query = cds.ql`SELECT from ClassroomsPupils { + const query = cds.ql`SELECT from ClassroomsPupils as ClassroomsPupils { pupil[ID = 5].ID as nonOptimized, pupil.ID as optimized, }` @@ -1320,7 +1320,7 @@ describe('optimize fk access', () => { expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('optimized next to join relevant', () => { - const query = cds.ql`SELECT from ClassroomsPupils { + const query = cds.ql`SELECT from ClassroomsPupils as ClassroomsPupils { classroom.ID as classroom_ID, classroom.name as classroom, }` @@ -1334,7 +1334,7 @@ describe('optimize fk access', () => { expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('two step path ends in foreign key simple ref in aggregation clauses', () => { - const query = cds.ql`SELECT from Classrooms { + const query = cds.ql`SELECT from Classrooms as Classrooms { pupils.pupil.ID as studentCount, } where pupils.pupil.ID = 1 @@ -1354,7 +1354,7 @@ describe('optimize fk access', () => { expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('two step path ends in foreign key nested ref', () => { - const query = cds.ql`SELECT from Classrooms { + const query = cds.ql`SELECT from Classrooms as Classrooms{ count(pupils.pupil.ID) as studentCount, } where Classrooms.ID = 1` const expected = cds.ql`SELECT from Classrooms as Classrooms left join ClassroomsPupils as pupils @@ -1366,7 +1366,7 @@ describe('optimize fk access', () => { }) it('multi step path ends in foreign key', () => { - const query = cds.ql`SELECT from Classrooms { + const query = cds.ql`SELECT from Classrooms as Classrooms { count(pupils.pupil.classrooms.classroom.ID) as classCount, } where pupils.pupil.classrooms.classroom.ID = 1 order by pupils.pupil.classrooms.classroom.ID` diff --git a/db-service/test/cqn4sql/basic.test.js b/db-service/test/cqn4sql/basic.test.js index 12b53e4e3..c3a37f9cb 100644 --- a/db-service/test/cqn4sql/basic.test.js +++ b/db-service/test/cqn4sql/basic.test.js @@ -13,7 +13,7 @@ describe('query clauses', () => { }) it('limit + offset', () => { - const original = cds.ql`SELECT from bookshop.Books { ID } limit 50 offset 25` + const original = cds.ql`SELECT from bookshop.Books as Books { ID } limit 50 offset 25` expect(cqn4sql(original, model)).to.deep.equal( cds.ql` SELECT from bookshop.Books as Books { Books.ID } limit 50 offset 25 @@ -23,7 +23,7 @@ describe('query clauses', () => { it('`sort` and `nulls` are passed along in order by', () => { const original = cds.ql` - SELECT from bookshop.Books { ID } + SELECT from bookshop.Books as Books { ID } order by Books.ID asc nulls first, 1 + 1 desc nulls last, func() desc nulls first @@ -38,7 +38,7 @@ describe('query clauses', () => { ) }) it('`sort` and `nulls` are passed along also to flat field in order by', () => { - const original = cds.ql`SELECT from bookshop.Books { ID } order by Books.dedication.sub asc nulls first` + const original = cds.ql`SELECT from bookshop.Books as Books { ID } order by Books.dedication.sub asc nulls first` expect(cqn4sql(original, model)).to.deep.equal( cds.ql` SELECT from bookshop.Books as Books { Books.ID } @@ -48,7 +48,7 @@ describe('query clauses', () => { }) it('preserves cast property on column', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Bar { + cds.ql`SELECT from bookshop.Bar as Bar { ID as castedID: cds.String }`, model, @@ -62,7 +62,7 @@ describe('query clauses', () => { // hence we should make the alias explicit for the cds style cast it('adds explicit alias for cast property on column', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Bar { + cds.ql`SELECT from bookshop.Bar as Bar { ID: cds.String }`, model, diff --git a/db-service/test/cqn4sql/calculated-elements.test.js b/db-service/test/cqn4sql/calculated-elements.test.js index e9207b77e..1ceab0c94 100644 --- a/db-service/test/cqn4sql/calculated-elements.test.js +++ b/db-service/test/cqn4sql/calculated-elements.test.js @@ -11,7 +11,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('simple reference', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, stock2 }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, stock2 }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, Books.stock as stock2 @@ -20,7 +20,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('simple val', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors { ID, IBAN }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors as Authors { ID, IBAN }`, model) const expected = cds.ql`SELECT from booksCalc.Authors as Authors { Authors.ID, 'DE' || Authors.checksum || Authors.sortCode || Authors.accountNumber as IBAN @@ -29,7 +29,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('directly', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, area }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, area }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, Books.length * Books.width as area @@ -38,7 +38,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('in expression', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, stock * area as f }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, stock * area as f }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, Books.stock * ( Books.length * Books.width ) as f @@ -47,7 +47,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('in ternary', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Ternary { ID, nestedTernary }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Ternary as Ternary { ID, nestedTernary }`, model) const expected = cds.ql`SELECT from booksCalc.Ternary as Ternary left join booksCalc.Books as book on book.ID = Ternary.book_ID { @@ -58,7 +58,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('calcualted element in nested ternary', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Ternary { ID, calculatedElementInNestedTernary }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Ternary as Ternary { ID, calculatedElementInNestedTernary }`, model) const expected = cds.ql`SELECT from booksCalc.Ternary as Ternary left join booksCalc.Books as book on book.ID = Ternary.book_ID left join booksCalc.Authors as author on author.ID = book.author_ID @@ -70,7 +70,7 @@ describe('Unfolding calculated elements in select list', () => { expect(query).to.deep.equal(expected) }) it('list in ternary', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Ternary { ID, nestedTernaryWithNestedXpr }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Ternary as Ternary { ID, nestedTernaryWithNestedXpr }`, model) const expected = cds.ql`SELECT from booksCalc.Ternary as Ternary left join booksCalc.Books as book on book.ID = Ternary.book_ID { @@ -80,7 +80,7 @@ describe('Unfolding calculated elements in select list', () => { expect(query).to.deep.equal(expected) }) it('in function', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, round(area, 2) as f }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, round(area, 2) as f }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, round(Books.length * Books.width, 2) as f @@ -88,7 +88,7 @@ describe('Unfolding calculated elements in select list', () => { expect(query).to.deep.equal(expected) }) it('in function with named param', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors { ID, ageNamedParams as f }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors as Authors { ID, ageNamedParams as f }`, model) const expected = cds.ql`SELECT from booksCalc.Authors as Authors { Authors.ID, years_between(DOB => Authors.dateOfBirth, DOD => Authors.dateOfDeath) as f @@ -97,7 +97,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('calc elem is function', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, ctitle }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, ctitle }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, substring(Books.title, 3, Books.stock) as ctitle @@ -105,7 +105,7 @@ 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(cds.ql`SELECT from booksCalc.Books { ID, authorAgeNativePG }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, authorAgeNativePG }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books left join booksCalc.Authors as author on author.ID = Books.author_ID { @@ -115,7 +115,7 @@ describe('Unfolding calculated elements in select list', () => { expect(query).to.deep.equal(expected) }) it('calc elem is xpr with nested xpr which has multiple functions as args', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, authorAgeInDogYears }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, authorAgeInDogYears }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books left join booksCalc.Authors as author on author.ID = Books.author_ID { @@ -125,7 +125,7 @@ describe('Unfolding calculated elements in select list', () => { expect(query).to.deep.equal(expected) }) it('calc elem is xpr with multiple functions as args - back and forth', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, author.books.authorAgeNativePG }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, author.books.authorAgeNativePG }`, model) 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 @@ -138,7 +138,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('calc elem is function, nested in direct expression', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, ctitle || title as f }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, ctitle || title as f }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, substring(Books.title, 3, Books.stock) || Books.title as f @@ -147,7 +147,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('nested calc elems', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, volume, storageVolume }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, volume, storageVolume }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, (Books.length * Books.width) * Books.height as volume, @@ -157,7 +157,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('nested calc elems, nested in direct expression', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, storageVolume / volume as f }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, storageVolume / volume as f }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, (Books.stock * ((Books.length * Books.width) * Books.height)) @@ -171,7 +171,7 @@ describe('Unfolding calculated elements in select list', () => { // it('via an association path', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, author.name }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, author.name }`, model) // revisit: alias follows our "regular" naming scheme -> ref.join('_') const expected = cds.ql`SELECT from booksCalc.Books as Books left outer join booksCalc.Authors as author on author.ID = Books.author_ID { @@ -182,7 +182,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('via an association in columns and where', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, author.name } where author.name like '%Bro%'`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, author.name } where author.name like '%Bro%'`, model) // revisit: alias follows our "regular" naming scheme -> ref.join('_') const expected = cds.ql`SELECT from booksCalc.Books as Books left outer join booksCalc.Authors as author on author.ID = Books.author_ID { @@ -193,7 +193,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('via an association path, nested in direct expression', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, substring(author.name, 2, stock) as f }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, substring(author.name, 2, stock) as f }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books left outer join booksCalc.Authors as author on author.ID = Books.author_ID { Books.ID, @@ -203,7 +203,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('via two association paths', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors { ID, books[stock<5].area, books[stock>5].area as a2}`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors as Authors { ID, books[stock<5].area, books[stock>5].area as a2}`, model) 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.stock < 5 left outer join booksCalc.Books as books2 on books2.author_ID = Authors.ID and books2.stock > 5 @@ -216,7 +216,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('in filter', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors { ID, books[area >17].title }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors as Authors { ID, books[area >17].title }`, model) // intermediate: // SELECT from booksCalc.Authors { ID, books[(length * width) > 1].title } const expected = cds.ql`SELECT from booksCalc.Authors as Authors @@ -230,7 +230,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('calc elem contains association', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, authorName, authorLastName }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, authorName, authorLastName }`, model) // intermediate: // SELECT from booksCalc.Books { ID, author.name, author.lastName } const expected = cds.ql`SELECT from booksCalc.Books as Books @@ -244,7 +244,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('calc elem contains associations in xpr', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, authorFullName }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, authorFullName }`, model) // intermediate: // SELECT from booksCalc.Books { ID, author.name, author.lastName } const expected = cds.ql`SELECT from booksCalc.Books as Books @@ -258,7 +258,7 @@ describe('Unfolding calculated elements in select list', () => { it('calc elem contains other calculated element in xpr with nested joins', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { ID, authorFullNameWithAddress } where authorFullNameWithAddress = 'foo'`, + cds.ql`SELECT from booksCalc.Books as Books { ID, authorFullNameWithAddress } where authorFullNameWithAddress = 'foo'`, model, ) // intermediate: @@ -275,7 +275,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('calc elem contains association, nested', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, authorAdrText }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, authorAdrText }`, model) // intermediate: // SELECT from booksCalc.Books { ID, author.address.{street || ', ' || city} } const expected = cds.ql`SELECT from booksCalc.Books as Books @@ -289,7 +289,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('calc elem contains association with filter', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors { ID, addressTextFilter }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors as Authors { ID, addressTextFilter }`, model) // intermediate: // SELECT from booksCalc.Authors { ID, address[number * 2 > 17].{street || ', ' || city} } const expected = cds.ql`SELECT from booksCalc.Authors as Authors @@ -306,7 +306,7 @@ describe('Unfolding calculated elements in select list', () => { // inline, expand // it('in inline', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, author.{name, IBAN } }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, author.{name, IBAN } }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books left outer join booksCalc.Authors as author on author.ID = Books.author_ID { @@ -317,7 +317,7 @@ describe('Unfolding calculated elements in select list', () => { expect(query).to.deep.equal(expected) }) it('in inline back and forth', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, author.books.author.{name, IBAN } }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, author.books.author.{name, IBAN } }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books left outer join booksCalc.Authors as author on author.ID = Books.author_ID left outer join booksCalc.Books as books2 on books2.author_ID = author.ID @@ -331,10 +331,10 @@ describe('Unfolding calculated elements in select list', () => { }) it('in subquery, using the same calc element - not join relevant in subquery', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { + cds.ql`SELECT from booksCalc.Books as Books { ID, ( - SELECT from booksCalc.Authors { + SELECT from booksCalc.Authors as Authors { name, IBAN, addressText @@ -367,7 +367,7 @@ describe('Unfolding calculated elements in select list', () => { }) it('in inline, 2 assocs', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, author.{name, addressText } }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, author.{name, addressText } }`, model) // intermediate: // SELECT from booksCalc.Authors { ID, author.{firstName || ' ' || lastName, address.{street || ', ' || city}}} } const expected = cds.ql`SELECT from booksCalc.Books as Books @@ -382,39 +382,39 @@ describe('Unfolding calculated elements in select list', () => { }) it('in expand (to-one)', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, author {name, IBAN } }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, author {name, IBAN } }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, ( - SELECT from booksCalc.Authors as author { - author.firstName || ' ' || author.lastName as name, - 'DE' || author.checksum || author.sortCode || author.accountNumber as IBAN - } where Books.author_ID = author.ID + SELECT from booksCalc.Authors as $a { + $a.firstName || ' ' || $a.lastName as name, + 'DE' || $a.checksum || $a.sortCode || $a.accountNumber as IBAN + } where Books.author_ID = $a.ID ) as author }` expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected) }) it('in expand (to-one), 2 assocs', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, author {name, addressText } }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, author {name, addressText } }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, ( - SELECT from booksCalc.Authors as author - left join booksCalc.Addresses as address on address.ID = author.address_ID + SELECT from booksCalc.Authors as $a + left join booksCalc.Addresses as address on address.ID = $a.address_ID { - author.firstName || ' ' || author.lastName as name, + $a.firstName || ' ' || $a.lastName as name, address.street || ', ' || address.city as addressText - } where Books.author_ID = author.ID + } where Books.author_ID = $a.ID ) as author }` expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected) }) it('expand and inline target same calc element', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { ID, author.{name, addressText }, author {name, addressText } }`, + cds.ql`SELECT from booksCalc.Books as Books { ID, author.{name, addressText }, author {name, addressText } }`, model, ) const expected = cds.ql`SELECT from booksCalc.Books as Books @@ -425,19 +425,19 @@ describe('Unfolding calculated elements in select list', () => { author.firstName || ' ' || author.lastName as author_name, address.street || ', ' || address.city as author_addressText, ( - SELECT from booksCalc.Authors as author2 - left join booksCalc.Addresses as address2 on address2.ID = author2.address_ID + SELECT from booksCalc.Authors as $a + left join booksCalc.Addresses as address2 on address2.ID = $a.address_ID { - author2.firstName || ' ' || author2.lastName as name, + $a.firstName || ' ' || $a.lastName as name, address2.street || ', ' || address2.city as addressText - } where Books.author_ID = author2.ID + } where Books.author_ID = $a.ID ) as author }` expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected) }) it('expand and inline target same calc element inverted', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { ID, author {name, addressText }, author.{name, addressText } }`, + cds.ql`SELECT from booksCalc.Books as Books { ID, author {name, addressText }, author.{name, addressText } }`, model, ) const expected = cds.ql`SELECT from booksCalc.Books as Books @@ -446,12 +446,12 @@ describe('Unfolding calculated elements in select list', () => { { Books.ID, ( - SELECT from booksCalc.Authors as author2 - left join booksCalc.Addresses as address2 on address2.ID = author2.address_ID + SELECT from booksCalc.Authors as $a + left join booksCalc.Addresses as address2 on address2.ID = $a.address_ID { - author2.firstName || ' ' || author2.lastName as name, + $a.firstName || ' ' || $a.lastName as name, address2.street || ', ' || address2.city as addressText - } where Books.author_ID = author2.ID + } where Books.author_ID = $a.ID ) as author, author.firstName || ' ' || author.lastName as author_name, address.street || ', ' || address.city as author_addressText @@ -462,15 +462,15 @@ describe('Unfolding calculated elements in select list', () => { it('in expand (to-many)', () => { let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors { ID, books { ID, area, volume } }`, model) - const expected = cds.ql`SELECT from booksCalc.Authors as Authors { - Authors.ID, + const expected = cds.ql`SELECT from booksCalc.Authors as $A { + $A.ID, ( - SELECT from booksCalc.Books as books + SELECT from booksCalc.Books as $b { - books.ID, - books.length * books.width as area, - (books.length * books.width) * books.height as volume, - } where Authors.ID = books.author_ID + $b.ID, + $b.length * $b.width as area, + ($b.length * $b.width) * $b.height as volume, + } where $A.ID = $b.author_ID ) as books }` expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected) @@ -482,7 +482,7 @@ describe('Unfolding calculated elements in select list', () => { it('via wildcard without columns', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books excluding { length, width, height, stock, price, youngAuthorName }`, + cds.ql`SELECT from booksCalc.Books as Books excluding { length, width, height, stock, price, youngAuthorName }`, model, ) const expected = cds.ql`SELECT from booksCalc.Books as Books @@ -517,7 +517,7 @@ describe('Unfolding calculated elements in select list', () => { it('via wildcard', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { * } excluding { length, width, height, stock, price, youngAuthorName}`, + cds.ql`SELECT from booksCalc.Books as Books { * } excluding { length, width, height, stock, price, youngAuthorName}`, model, ) const expected = cds.ql`SELECT from booksCalc.Books as Books @@ -554,12 +554,12 @@ describe('Unfolding calculated elements in select list', () => { let query = cqn4sql(cds.ql`SELECT from ( SELECT FROM booksCalc.Simple { * } )`, model) const expected = cds.ql` SELECT from ( - SELECT from booksCalc.Simple as Simple - left join booksCalc.Simple as my on my.ID = Simple.my_ID + SELECT from booksCalc.Simple as $S + left join booksCalc.Simple as my on my.ID = $S.my_ID { - Simple.ID, - Simple.name, - Simple.my_ID, + $S.ID, + $S.name, + $S.my_ID, my.name as myName } ) as __select__ { @@ -581,12 +581,12 @@ describe('Unfolding calculated elements in select list', () => { ) const expected = cds.ql` SELECT from ( - SELECT from booksCalc.Simple as Simple - left join booksCalc.Simple as my2 on my2.ID = Simple.my_ID + SELECT from booksCalc.Simple as $S + left join booksCalc.Simple as my2 on my2.ID = $S.my_ID { - Simple.ID, - Simple.name, - Simple.my_ID, + $S.ID, + $S.name, + $S.my_ID, my2.name as myName } ) as __select__ left join booksCalc.Simple as my on my.ID = __select__.my_ID { @@ -598,7 +598,7 @@ describe('Unfolding calculated elements in select list', () => { it('replacement for calculated element is considered for wildcard expansion', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { *, volume as ctitle } excluding { length, width, height, stock, price, youngAuthorName }`, + cds.ql`SELECT from booksCalc.Books as Books { *, volume as ctitle } excluding { length, width, height, stock, price, youngAuthorName }`, model, ) const expected = cds.ql`SELECT from booksCalc.Books as Books @@ -638,7 +638,7 @@ describe('Unfolding calculated elements in select list', () => { // existing join node let query = cqn4sql( cds.ql` - SELECT from booksCalc.Books { + SELECT from booksCalc.Books as Books { youngAuthorName, authorLastName, authorName, @@ -671,7 +671,7 @@ describe('Unfolding calculated elements in select list', () => { // existing join node let query = cqn4sql( cds.ql` - SELECT from booksCalc.Authors { + SELECT from booksCalc.Authors as Authors { books.youngAuthorName, books.authorLastName, books.authorName, @@ -705,7 +705,7 @@ describe('Unfolding calculated elements in select list', () => { // existing join node let query = cqn4sql( cds.ql` - SELECT from booksCalc.Authors { + SELECT from booksCalc.Authors as Authors { books.author.books.youngAuthorName, books.author.books.authorLastName, books.author.books.authorName, @@ -777,7 +777,7 @@ describe('Unfolding calculated elements in select list', () => { it('via wildcard in expand subquery include complex calc element', () => { let query = cqn4sql( cds.ql` - SELECT from booksCalc.Authors { + SELECT from booksCalc.Authors as Authors { books { * } excluding { length, width, height, stock, price} } `, @@ -786,24 +786,24 @@ describe('Unfolding calculated elements in select list', () => { const expected = cds.ql`SELECT from booksCalc.Authors as Authors { ( - SELECT from booksCalc.Books as books - left outer join booksCalc.Authors as author on author.ID = books.author_ID + SELECT from booksCalc.Books as $b + left outer join booksCalc.Authors as author on author.ID = $b.author_ID and years_between(author.dateOfBirth, author.dateOfDeath) < 50 - left outer join booksCalc.Authors as author2 on author2.ID = books.author_ID + left outer join booksCalc.Authors as author2 on author2.ID = $b.author_ID left outer join booksCalc.Addresses as address on address.ID = author2.address_ID { - books.ID, - books.title, - books.author_ID, + $b.ID, + $b.title, + $b.author_ID, - books.stock as stock2, - substring(books.title, 3, books.stock) as ctitle, + $b.stock as stock2, + substring($b.title, 3, $b.stock) as ctitle, - books.areaS, + $b.areaS, - books.length * books.width as area, - (books.length * books.width) * books.height as volume, - books.stock * ((books.length * books.width) * books.height) as storageVolume, + $b.length * $b.width as area, + ($b.length * $b.width) * $b.height as volume, + $b.stock * (($b.length * $b.width) * $b.height) as storageVolume, author.firstName || ' ' || author.lastName as youngAuthorName, author2.lastName as authorLastName, @@ -816,7 +816,7 @@ describe('Unfolding calculated elements in select list', () => { 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 + } where Authors.ID = $b.author_ID ) as books }` expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected) @@ -824,7 +824,7 @@ describe('Unfolding calculated elements in select list', () => { it('via wildcard in expand subquery', () => { let query = cqn4sql( cds.ql` - SELECT from booksCalc.Authors { + SELECT from booksCalc.Authors as Authors { books { * } excluding { length, width, height, stock, price, youngAuthorName} } `, @@ -833,22 +833,22 @@ describe('Unfolding calculated elements in select list', () => { const expected = cds.ql`SELECT from booksCalc.Authors as Authors { ( - SELECT from booksCalc.Books as books - left outer join booksCalc.Authors as author on author.ID = books.author_ID + SELECT from booksCalc.Books as $b + left outer join booksCalc.Authors as author on author.ID = $b.author_ID left outer join booksCalc.Addresses as address on address.ID = author.address_ID { - books.ID, - books.title, - books.author_ID, + $b.ID, + $b.title, + $b.author_ID, - books.stock as stock2, - substring(books.title, 3, books.stock) as ctitle, + $b.stock as stock2, + substring($b.title, 3, $b.stock) as ctitle, - books.areaS, + $b.areaS, - books.length * books.width as area, - (books.length * books.width) * books.height as volume, - books.stock * ((books.length * books.width) * books.height) as storageVolume, + $b.length * $b.width as area, + ($b.length * $b.width) * $b.height as volume, + $b.stock * (($b.length * $b.width) * $b.height) as storageVolume, author.lastName as authorLastName, author.firstName || ' ' || author.lastName as authorName, @@ -860,7 +860,7 @@ describe('Unfolding calculated elements in select list', () => { 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 + } where Authors.ID = $b.author_ID ) as books }` expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected) @@ -874,7 +874,7 @@ describe('Unfolding calculated elements in other places', () => { }) it('in where', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID } where area < 13`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID } where area < 13`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID } where (Books.length * Books.width) < 13 ` @@ -883,10 +883,10 @@ describe('Unfolding calculated elements in other places', () => { it('in filter in where exists', () => { let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors { ID } where exists books[area < 13]`, model) - const expected = cds.ql`SELECT from booksCalc.Authors as Authors { Authors.ID } + const expected = cds.ql`SELECT from booksCalc.Authors as $A { $A.ID } where exists ( - select 1 from booksCalc.Books as books where books.author_ID = Authors.ID - and (books.length * books.width) < 13 + select 1 from booksCalc.Books as $b where $b.author_ID = $A.ID + and ($b.length * $b.width) < 13 ) ` expect(query).to.deep.equal(expected) @@ -894,7 +894,7 @@ describe('Unfolding calculated elements in other places', () => { it('in group by & having', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { ID, sum(price) as tprice } + cds.ql`SELECT from booksCalc.Books as Books { ID, sum(price) as tprice } group by ctitle having ctitle like 'A%'`, model, ) @@ -907,7 +907,7 @@ describe('Unfolding calculated elements in other places', () => { }) it('in order by', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, title } order by ctitle`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, title } order by ctitle`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, Books.title } order by substring(Books.title, 3, Books.stock) @@ -917,19 +917,19 @@ describe('Unfolding calculated elements in other places', () => { it('in filter in path in FROM', () => { let query = cqn4sql(cds.ql`SELECT from booksCalc.Authors[name like 'A%'].books[storageVolume < 4] { ID }`, model) - const expected = cds.ql`SELECT from booksCalc.Books as books { - books.ID - } where exists (select 1 from booksCalc.Authors as Authors - where Authors.ID = books.author_ID - and (Authors.firstName || ' ' || Authors.lastName) like 'A%') - and (books.stock * ((books.length * books.width) * books.height)) < 4 + 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 ` expect(query).to.deep.equal(expected) }) it('in a subquery', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { + cds.ql`SELECT from booksCalc.Books as Books { ID, (select from booksCalc.Authors as A { name } where A.ID = Books.author.ID and A.IBAN = Books.area + Books.ctitle) as f @@ -948,7 +948,7 @@ describe('Unfolding calculated elements in other places', () => { }) it('in a function, args are join relevant', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { + cds.ql`SELECT from booksCalc.Books as Books { ID, authorAge }`, @@ -966,7 +966,7 @@ describe('Unfolding calculated elements in other places', () => { }) it('calculated element has other calc element in infix filter', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { + cds.ql`SELECT from booksCalc.Books as Books { ID, youngAuthorName }`, @@ -985,7 +985,7 @@ describe('Unfolding calculated elements in other places', () => { }) it('in a subquery calc element is join relevant', () => { let query = cqn4sql( - cds.ql`SELECT from booksCalc.Books { + cds.ql`SELECT from booksCalc.Books as Books { ID, (select from booksCalc.Authors as A { books.title } where A.ID = Books.author.ID and A.IBAN = Books.area + Books.ctitle) as f @@ -1005,7 +1005,7 @@ describe('Unfolding calculated elements in other places', () => { expect(query).to.deep.equal(expected) }) it('variable replacements are left untouched in calc element navigation', () => { - const q = cds.ql`SELECT from booksCalc.VariableReplacements { ID, authorAlive.firstName }` + const q = cds.ql`SELECT from booksCalc.VariableReplacements as VariableReplacements { ID, authorAlive.firstName }` const expected = cds.ql`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 $user.unknown.foo.bar = 'Bob' ) @@ -1016,7 +1016,7 @@ describe('Unfolding calculated elements in other places', () => { expect(cqn4sql(q, model)).to.deep.equal(expected) }) it('variable replacements are left untouched in calc elements via wildcard', () => { - const q = cds.ql`SELECT from booksCalc.VariableReplacements { * }` + const q = cds.ql`SELECT from booksCalc.VariableReplacements as VariableReplacements { * }` const expected = cds.ql`SELECT from booksCalc.VariableReplacements as VariableReplacements { VariableReplacements.ID, @@ -1026,16 +1026,16 @@ describe('Unfolding calculated elements in other places', () => { }) it('with expand', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.VariableReplacements { ID, authorAlive { ID } }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.VariableReplacements as VariableReplacements { ID, authorAlive { ID } }`, model) const expected = cds.ql`SELECT from booksCalc.VariableReplacements as VariableReplacements { VariableReplacements.ID, ( - SELECT from booksCalc.Authors as authorAlive + SELECT from booksCalc.Authors as $a { - authorAlive.ID, + $a.ID, } - where (authorAlive.ID = VariableReplacements.author_ID) - and ( authorAlive.dateOfBirth <= $now and authorAlive.dateOfDeath >= $now and $user.unknown.foo.bar = 'Bob' ) + where ($a.ID = VariableReplacements.author_ID) + and ( $a.dateOfBirth <= $now and $a.dateOfDeath >= $now and $user.unknown.foo.bar = 'Bob' ) ) as authorAlive }` expect(JSON.parse(JSON.stringify(query))).to.deep.equal(expected) @@ -1048,7 +1048,7 @@ describe('Unfolding calculated elements ... misc', () => { model = cds.model = await cds.load(__dirname + '/../bookshop/db/booksWithExpr').then(cds.linked) }) it('calculated element on-write (stored) is not unfolded', () => { - let query = cqn4sql(cds.ql`SELECT from booksCalc.Books { ID, areaS }`, model) + let query = cqn4sql(cds.ql`SELECT from booksCalc.Books as Books { ID, areaS }`, model) const expected = cds.ql`SELECT from booksCalc.Books as Books { Books.ID, Books.areaS }` expect(query).to.deep.equal(expected) }) @@ -1062,7 +1062,7 @@ describe('Unfolding calculated elements and localized', () => { }) it('presence of localized element should not affect unfolding', () => { - const q = cds.ql`SELECT from booksCalc.LBooks { ID, title, area }` + const q = cds.ql`SELECT from booksCalc.LBooks as LBooks { ID, title, area }` q.SELECT.localized = true let query = cqn4sql(q, model) const expected = cds.ql`SELECT from localized.booksCalc.LBooks as LBooks { @@ -1075,7 +1075,7 @@ describe('Unfolding calculated elements and localized', () => { }) it('calculated element refers to localized element', () => { - const q = cds.ql`SELECT from booksCalc.LBooks { ID, title, ctitle }` + const q = cds.ql`SELECT from booksCalc.LBooks as LBooks { ID, title, ctitle }` q.SELECT.localized = true let query = cqn4sql(q, model) const expected = cds.ql`SELECT from localized.booksCalc.LBooks as LBooks { diff --git a/db-service/test/cqn4sql/column.element.test.js b/db-service/test/cqn4sql/column.element.test.js index 941ffc543..c98ab6a6b 100644 --- a/db-service/test/cqn4sql/column.element.test.js +++ b/db-service/test/cqn4sql/column.element.test.js @@ -14,7 +14,7 @@ describe('assign element onto columns', () => { it('attaches the `element` to simple / structured columns', () => { let query = cqn4sql(cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { ID, author, dedication.addressee, @@ -73,7 +73,7 @@ describe('assign element onto columns with flat model', () => { it('foreign key is adjacent to its association in flat model', () => { let query = cqn4sql(cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { ID, author } @@ -91,7 +91,7 @@ describe('assign element onto columns with flat model', () => { }) it('within expand, the key in the target is attached, not the foreign key', () => { let query = cqn4sql(cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { ID, author { ID @@ -100,9 +100,9 @@ describe('assign element onto columns with flat model', () => { `, model) const expected = cds.ql`SELECT from bookshop.Books as Books { Books.ID, - (SELECT from bookshop.Authors as author { - author.ID - } where Books.author_ID = author.ID) as author + (SELECT from bookshop.Authors as $a { + $a.ID + } where Books.author_ID = $a.ID) as author } ` const { Authors } = model.entities @@ -113,7 +113,7 @@ describe('assign element onto columns with flat model', () => { it('foreign key is adjacent to its association in flat model with multiple foreign keys', () => { let query = cqn4sql(cds.ql` - SELECT from bookshop.AssocWithStructuredKey { + SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { ID, toStructuredKey } @@ -144,7 +144,7 @@ describe('assign element onto columns with flat model', () => { it('foreign key is adjacent to its association in flat model and is renamed', () => { let query = cqn4sql(cds.ql` - SELECT from bookshop.TestPublisher { + SELECT from bookshop.TestPublisher as TestPublisher { ID, publisherRenamedKey } diff --git a/db-service/test/cqn4sql/compare-structs.test.js b/db-service/test/cqn4sql/compare-structs.test.js index f88169330..55ee651f4 100644 --- a/db-service/test/cqn4sql/compare-structs.test.js +++ b/db-service/test/cqn4sql/compare-structs.test.js @@ -21,8 +21,8 @@ describe('compare structures', () => { it('expand NULL with a managed association in where w/ parens', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.AssocWithStructuredKey { ID } where not AssocWithStructuredKey.toStructuredKey ${first} null` - let query = cqn4sql(CQL(queryString), model) + const queryString = `SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { ID } where not AssocWithStructuredKey.toStructuredKey ${first} null` + let query = cqn4sql(cds.ql(queryString), model) const expectedQueryString = ` SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { AssocWithStructuredKey.ID } where not (AssocWithStructuredKey.toStructuredKey_struct_mid_leaf ${first} null AND @@ -34,7 +34,7 @@ describe('compare structures', () => { it('expand NULL with a managed association in where w/o parens', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.AssocWithStructuredKey { ID } where AssocWithStructuredKey.toStructuredKey ${first} null` + const queryString = `SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { ID } where AssocWithStructuredKey.toStructuredKey ${first} null` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { AssocWithStructuredKey.ID } @@ -47,7 +47,7 @@ describe('compare structures', () => { it('expand NULL with a managed association in having w/ parens', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.AssocWithStructuredKey { ID } having not AssocWithStructuredKey.toStructuredKey ${first} null` + const queryString = `SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { ID } having not AssocWithStructuredKey.toStructuredKey ${first} null` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { AssocWithStructuredKey.ID } @@ -61,7 +61,7 @@ describe('compare structures', () => { it('expand NULL with a managed association in where w/ parens', () => { notEqOps.forEach(op => { const [first, second] = op - const queryString = `SELECT from bookshop.AssocWithStructuredKey { ID } where not AssocWithStructuredKey.toStructuredKey ${ + const queryString = `SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { ID } where not AssocWithStructuredKey.toStructuredKey ${ second ? first + ' ' + second : first } null` let query = cqn4sql(CQL(queryString), model) @@ -81,7 +81,7 @@ describe('compare structures', () => { it('expand NULL with a managed association in having and omits xpr if possible', () => { eqOps.forEach(op => { const [first, second] = op - const queryString = `SELECT from bookshop.Books { ID } having Books.author ${ + const queryString = `SELECT from bookshop.Books as Books { ID } having Books.author ${ second ? first + ' ' + second : first } null` let query = cqn4sql(CQL(queryString), model) @@ -99,7 +99,7 @@ describe('compare structures', () => { it.skip('MUST expand NULL with a managed association in having if operands are flipped?', () => { eqOps.forEach(op => { const [first, second] = op - const queryString = `SELECT from bookshop.Books { ID } having null ${ + const queryString = `SELECT from bookshop.Books as Books { ID } having null ${ second ? first + ' ' + second : first } Books.author` let query = cqn4sql(CQL(queryString), model) @@ -111,17 +111,17 @@ describe('compare structures', () => { }) it('list in where', () => { - let query = cds.ql`SELECT from bookshop.Books { ID } where (author.ID, 1) in ('foo', 'bar')` + let query = cds.ql`SELECT from bookshop.Books as Books { ID } where (author.ID, 1) in ('foo', 'bar')` let expected = cds.ql`SELECT from bookshop.Books as Books { Books.ID } where (Books.author_ID, 1) in ('foo', 'bar')` expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('tuple list in where', () => { - let query = cds.ql`SELECT from bookshop.Books { ID } where (author.ID, 1) in (('foo', 1), ('bar', 2))` + let query = cds.ql`SELECT from bookshop.Books as Books { ID } where (author.ID, 1) in (('foo', 1), ('bar', 2))` let expected = cds.ql`SELECT from bookshop.Books as Books { Books.ID } where (Books.author_ID, 1) in (('foo', 1), ('bar', 2))` expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('list in having', () => { - let query = cds.ql`SELECT from bookshop.Books { ID } having (author.ID, 1) in ('foo', 'bar')` + let query = cds.ql`SELECT from bookshop.Books as Books { ID } having (author.ID, 1) in ('foo', 'bar')` let expected = cds.ql`SELECT from bookshop.Books as Books { Books.ID } having (Books.author_ID, 1) in ('foo', 'bar')` expect(cqn4sql(query, model)).to.deep.equal(expected) }) @@ -145,7 +145,7 @@ describe('compare structures', () => { }) it('IS NULL comparison with a structure', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Bar { ID } where Bar.structure is null`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Bar as Bar { ID } where Bar.structure is null`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Bar as Bar {Bar.ID} where (Bar.structure_foo is null and Bar.structure_baz is null)`) @@ -154,7 +154,7 @@ describe('compare structures', () => { it(' NULL comparison with a managed association in column list', () => { eqOps.forEach(op => { const [first, second] = op - const queryString = `SELECT from bookshop.Books { + const queryString = `SELECT from bookshop.Books as Books { ID, case when not author ${second ? first + ' ' + second : first} null then 'hit' end as c }` @@ -170,7 +170,7 @@ describe('compare structures', () => { it('issues a proper error for operators which are not supported', () => { notSupportedOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.AssocWithStructuredKey { ID } where not AssocWithStructuredKey.toStructuredKey ${first} null` + const queryString = `SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { ID } where not AssocWithStructuredKey.toStructuredKey ${first} null` expect(() => cqn4sql(CQL(queryString), model)).to.throw( `The operator "${first}" is not supported for structure comparison`, ) diff --git a/db-service/test/cqn4sql/expand.test.js b/db-service/test/cqn4sql/expand.test.js index 843d0dee3..6f0c332d7 100644 --- a/db-service/test/cqn4sql/expand.test.js +++ b/db-service/test/cqn4sql/expand.test.js @@ -12,7 +12,7 @@ describe('Unfold expands on structure', () => { cds.model = await cds.load(__dirname + '/../bookshop/db/schema').then(cds.linked) }) it('supports nested projections for structs', () => { - let query = cds.ql`SELECT from bookshop.Books { ID, dedication { addressee } }` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, dedication { addressee } }` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( cds.ql`SELECT from bookshop.Books as Books { Books.ID, @@ -21,7 +21,7 @@ describe('Unfold expands on structure', () => { ) }) it('supports deeply nested projections for structs', () => { - let query = cds.ql`SELECT from bookshop.Books { ID, dedication { addressee, sub { foo } } }` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, dedication { addressee, sub { foo } } }` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( cds.ql`SELECT from bookshop.Books as Books { Books.ID, @@ -31,7 +31,7 @@ describe('Unfold expands on structure', () => { ) }) it('supports deeply nested projections for structs w/ wildcard', () => { - let query = cds.ql`SELECT from bookshop.Books { ID, dedication { addressee, sub { * } } }` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, dedication { addressee, sub { * } } }` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( cds.ql`SELECT from bookshop.Books as Books { @@ -42,7 +42,7 @@ describe('Unfold expands on structure', () => { ) }) it('supports renaming', () => { - let query = cds.ql`SELECT from bookshop.Books { ID as foo, dedication as bubu { addressee, sub { * } } }` + let query = cds.ql`SELECT from bookshop.Books as Books { ID as foo, dedication as bubu { addressee, sub { * } } }` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( cds.ql`SELECT from bookshop.Books as Books { @@ -53,7 +53,7 @@ describe('Unfold expands on structure', () => { ) }) it('supports nested projections for structs w/ order by', () => { - let query = cds.ql`SELECT from bookshop.Books { ID, dedication as bubu { addressee, sub { * } } } order by bubu.sub.foo` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, dedication as bubu { addressee, sub { * } } } order by bubu.sub.foo` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( cds.ql`SELECT from bookshop.Books as Books { @@ -65,7 +65,7 @@ describe('Unfold expands on structure', () => { }) it('supports nested projections for structs with wildcard select and respects order', () => { - let query = cds.ql`SELECT from bookshop.Books { dedication {text, * } }` + let query = cds.ql`SELECT from bookshop.Books as Books { dedication {text, * } }` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( cds.ql`SELECT from bookshop.Books as Books { @@ -77,7 +77,7 @@ describe('Unfold expands on structure', () => { ) }) it('supports nested projections for structs with wildcard select', () => { - let query = cds.ql`SELECT from bookshop.Books { ID, dedication { * } }` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, dedication { * } }` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( cds.ql`SELECT from bookshop.Books as Books { Books.ID, @@ -89,7 +89,7 @@ describe('Unfold expands on structure', () => { ) }) it('supports nested projections for structs with smart wildcard', () => { - let query = cds.ql`SELECT from bookshop.Books { ID, dedication { *, 5 as text } }` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, dedication { *, 5 as text } }` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( cds.ql`SELECT from bookshop.Books as Books { @@ -106,9 +106,9 @@ describe('Unfold expands on structure', () => { let query = cds.ql`SELECT from bookshop.Books { ID, dedication { addressee.name } }` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( - cds.ql`SELECT from bookshop.Books as Books - left outer join bookshop.Person as addressee on addressee.ID = Books.dedication_addressee_ID { - Books.ID, + cds.ql`SELECT from bookshop.Books as $B + left outer join bookshop.Person as addressee on addressee.ID = $B.dedication_addressee_ID { + $B.ID, addressee.name as dedication_addressee_name }`, ) @@ -117,9 +117,9 @@ describe('Unfold expands on structure', () => { let query = cds.ql`SELECT from bookshop.Books { ID, dedication { addressee[ID=42].name } }` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( - cds.ql`SELECT from bookshop.Books as Books - left outer join bookshop.Person as addressee on addressee.ID = Books.dedication_addressee_ID and addressee.ID = 42 { - Books.ID, + cds.ql`SELECT from bookshop.Books as $B + left outer join bookshop.Person as addressee on addressee.ID = $B.dedication_addressee_ID and addressee.ID = 42 { + $B.ID, addressee.name as dedication_addressee_name }`, ) @@ -131,13 +131,13 @@ describe('Unfold expands on structure', () => { }` let transformed = cqn4sql(query) expect(JSON.parse(JSON.stringify(transformed))).to.deep.eql( - cds.ql`SELECT from bookshop.Books as Books { - Books.ID, - Books.dedication_text, + cds.ql`SELECT from bookshop.Books as $B { + $B.ID, + $B.dedication_text, ( - SELECT dedication_addressee.name - from bookshop.Person as dedication_addressee - where Books.dedication_addressee_ID = dedication_addressee.ID + SELECT $d.name + from bookshop.Person as $d + where $B.dedication_addressee_ID = $d.ID ) as dedication_addressee }`, ) @@ -147,12 +147,12 @@ describe('Unfold expands on structure', () => { let query = cds.ql`SELECT from bookshop.Books { dedication { 'first' as first, 'second' as sub, *, 5 as ![5], 'Baz' as text } }` let transformed = cqn4sql(query) expect(transformed).to.deep.eql( - cds.ql`SELECT from bookshop.Books as Books { + cds.ql`SELECT from bookshop.Books as $B { 'first' as dedication_first, 'second' as dedication_sub, - Books.dedication_addressee_ID, + $B.dedication_addressee_ID, 'Baz' as dedication_text, - Books.dedication_dedication, + $B.dedication_dedication, 5 as dedication_5 }`, ) @@ -165,16 +165,16 @@ describe('Unfold expands on structure', () => { }` let transformed = cqn4sql(query) expect(JSON.parse(JSON.stringify(transformed))).to.deep.eql( - cds.ql`SELECT from bookshop.Books as Books { - Books.ID, - Books.dedication_text, + cds.ql`SELECT from bookshop.Books as $B { + $B.ID, + $B.dedication_text, ( SELECT - dedication_addressee.name, - dedication_addressee.address_street, - dedication_addressee.address_city - from bookshop.Person as dedication_addressee - where Books.dedication_addressee_ID = dedication_addressee.ID + $d.name, + $d.address_street, + $d.address_city + from bookshop.Person as $d + where $B.dedication_addressee_ID = $d.ID ) as dedication_addressee }`, ) @@ -207,30 +207,15 @@ describe('Unfold expands on associations to special subselects', () => { const res = cqn4sql(q) expect(res.SELECT.columns[0].SELECT).to.have.property('expand').that.equals(true) expect(res.SELECT.columns[0].SELECT).to.have.property('one').that.equals(true) - const expected = cds.ql`SELECT from bookshop.Books as Books { + const expected = cds.ql`SELECT from bookshop.Books as $B { ( - SELECT from bookshop.Authors as author { - author.name - } where Books.author_ID = author.ID + SELECT from bookshop.Authors as $a { + $a.name + } where $B.author_ID = $a.ID ) as author }` expect(JSON.parse(JSON.stringify(res))) .to.deep.equal(expected) - .to.deep.equal({ - SELECT: { - from: { ref: ['bookshop.Books'], as: 'Books' }, - columns: [ - { - SELECT: { - from: { ref: ['bookshop.Authors'], as: 'author' }, - columns: [{ ref: ['author', 'name'] }], - where: [{ ref: ['Books', 'author_ID'] }, '=', { ref: ['author', 'ID'] }], - }, - as: 'author', - }, - ], - }, - }) }) it('do not loose additional properties on expand column', () => { const q = { @@ -266,12 +251,12 @@ describe('Unfold expands on associations to special subselects', () => { } const res = cqn4sql(q) - const expected = cds.ql`SELECT from bookshop.Books as Books { + const expected = cds.ql`SELECT from bookshop.Books as $B { ( - SELECT from bookshop.Authors as author { - author.name + SELECT from bookshop.Authors as $a { + $a.name } - where Books.author_ID = author.ID + where $B.author_ID = $a.ID order by name ASC limit 1 offset 1 @@ -283,13 +268,13 @@ describe('Unfold expands on associations to special subselects', () => { it('do not loose additional properties on expand column if defined in ref', () => { const q = cds.ql`SELECT from bookshop.Authors { books[order by price] { title } }` const res = cqn4sql(q) - const expected = cds.ql`SELECT from bookshop.Authors as Authors { + const expected = cds.ql`SELECT from bookshop.Authors as $A { ( - SELECT from bookshop.Books as books { - books.title + SELECT from bookshop.Books as $b { + $b.title } - where Authors.ID = books.author_ID - order by books.price + where $A.ID = $b.author_ID + order by $b.price ) as books }` expect(JSON.parse(JSON.stringify(res))).to.deep.equal(expected) @@ -330,13 +315,13 @@ describe('Unfold expands on associations to special subselects', () => { } const res = cqn4sql(q) - const expected = cds.ql`SELECT from bookshop.Books as Books { + const expected = cds.ql`SELECT from bookshop.Books as $B { ( - SELECT from bookshop.Authors as author { - author.name + SELECT from bookshop.Authors as $a { + $a.name } - where Books.author_ID = author.ID - order by author.dateOfDeath asc, author.dateOfBirth desc + where $B.author_ID = $a.ID + order by $a.dateOfDeath asc, $a.dateOfBirth desc limit 1 offset 1 ) as author @@ -378,17 +363,17 @@ describe('Unfold expands on associations to special subselects', () => { }, } - const expected = cds.ql`SELECT from bookshop.Books as Books { + const expected = cds.ql`SELECT from bookshop.Books as $B { ( - SELECT from bookshop.Authors as author { - author.name + SELECT from bookshop.Authors as $a { + $a.name } - where Books.author_ID = author.ID and - exists ( SELECT 1 from bookshop.Books as books2 where - books2.author_ID = author.ID and exists ( - SELECT 1 from bookshop.Authors as author2 where - author2.ID = books2.author_ID and - author2.name = 'King' + where $B.author_ID = $a.ID and + exists ( SELECT 1 from bookshop.Books as $b2 where + $b2.author_ID = $a.ID and exists ( + SELECT 1 from bookshop.Authors as $a2 where + $a2.ID = $b2.author_ID and + $a2.name = 'King' ) ) order by name ASC @@ -418,9 +403,9 @@ describe('Unfold expands on associations to special subselects', () => { const qx = cds.ql`SELECT from bookshop.Books as NotBooks { NotBooks.ID, ( - SELECT texts.locale - from bookshop.Books.texts as texts - where texts.ID = NotBooks.ID + SELECT $t.locale + from bookshop.Books.texts as $t + where $t.ID = NotBooks.ID ) as texts }` const res = cqn4sql(q) @@ -431,9 +416,9 @@ describe('Unfold expands on associations to special subselects', () => { const q = cds.ql`SELECT from bookshop.Books { author[name='King' or name like '%Sanderson'] { name } }` - const qx = cds.ql`SELECT from bookshop.Books as Books { - (SELECT author.name from bookshop.Authors as author - where Books.author_ID = author.ID and (author.name = 'King' or author.name like '%Sanderson')) as author + const qx = cds.ql`SELECT from bookshop.Books as $B { + (SELECT $a.name from bookshop.Authors as $a + where $B.author_ID = $a.ID and ($a.name = 'King' or $a.name like '%Sanderson')) as author }` const res = cqn4sql(q) expect(res.SELECT.columns[0].SELECT).to.have.property('expand').that.equals(true) @@ -448,11 +433,11 @@ describe('Unfold expands on associations to special subselects', () => { } `) let expected = cds.ql` - SELECT from bookshop.SoccerTeams as SoccerTeams { + SELECT from bookshop.SoccerTeams as $S { ( - SELECT from bookshop.SoccerPlayers as goalKeeper { - goalKeeper.name - } where goalKeeper.jerseyNumber = 1 and (SoccerTeams.ID = goalKeeper.team_ID) + SELECT from bookshop.SoccerPlayers as $g { + $g.name + } where $g.jerseyNumber = 1 and ($S.ID = $g.team_ID) ) as goalKeeper } ` @@ -462,15 +447,15 @@ describe('Unfold expands on associations to special subselects', () => { // TODO: aliases of outer query needs to be considered // still valid sql in this case it('unfold expand, with subquery in expand', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books{ author { name, (select title from bookshop.Books) as book } }` - const qx = cds.ql`SELECT from bookshop.Books as Books { + const qx = cds.ql`SELECT from bookshop.Books as $B { (SELECT - author.name, - (select Books2.title from bookshop.Books as Books2) as book - from bookshop.Authors as author - where Books.author_ID = author.ID) as author + $a.name, + (select $B2.title from bookshop.Books as $B2) as book + from bookshop.Authors as $a + where $B.author_ID = $a.ID) as author }` const res = cqn4sql(q) expect(res.SELECT.columns[0].SELECT).to.have.property('expand').that.equals(true) @@ -482,23 +467,23 @@ describe('Unfold expands on associations to special subselects', () => { const q = cds.ql`SELECT from (select author from bookshop.Books) as book { author { name } }` - const qx = cds.ql`SELECT from (select Books.author_ID from bookshop.Books as Books) as book { + const qx = cds.ql`SELECT from (select $B.author_ID from bookshop.Books as $B) as book { (SELECT - author.name - from bookshop.Authors as author - where book.author_ID = author.ID) as author + $a.name + from bookshop.Authors as $a + where book.author_ID = $a.ID) as author }` const res = cqn4sql(q) expect(JSON.parse(JSON.stringify(res))).to.deep.eql(qx) }) it('unfold expand, several fields with alias', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { author { name, dateOfBirth as dob, placeOfBirth as pob} }` const qx = cds.ql`SELECT from bookshop.Books as Books { - (SELECT author.name, author.dateOfBirth as dob, author.placeOfBirth as pob - from bookshop.Authors as author where Books.author_ID = author.ID) as author + (SELECT $a.name, $a.dateOfBirth as dob, $a.placeOfBirth as pob + from bookshop.Authors as $a where Books.author_ID = $a.ID) as author }` const res = cqn4sql(q) expect(res.SELECT.columns[0].SELECT).to.have.property('expand').that.equals(true) @@ -507,12 +492,12 @@ describe('Unfold expands on associations to special subselects', () => { }) it('unfold expand, several fields with expressions', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { author { name, substring(placeOfBirth, 1, 1) as pob } }` const qx = cds.ql`SELECT from bookshop.Books as Books { - (SELECT author.name, substring(author.placeOfBirth, 1, 1) as pob - from bookshop.Authors as author where Books.author_ID = author.ID) as author + (SELECT $a.name, substring($a.placeOfBirth, 1, 1) as pob + from bookshop.Authors as $a where Books.author_ID = $a.ID) as author }` const res = cqn4sql(q) expect(res.SELECT.columns[0].SELECT).to.have.property('expand').that.equals(true) @@ -521,13 +506,13 @@ describe('Unfold expands on associations to special subselects', () => { }) it('unfold expand, structured field', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { author { name, address } }` const qx = cds.ql`SELECT from bookshop.Books as Books { ( SELECT - author.name, author.address_street, author.address_city - from bookshop.Authors as author where Books.author_ID = author.ID) as author + $a.name, $a.address_street, $a.address_city + from bookshop.Authors as $a where Books.author_ID = $a.ID) as author }` const res = cqn4sql(q) expect(res.SELECT.columns[0].SELECT).to.have.property('expand').that.equals(true) @@ -535,13 +520,13 @@ describe('Unfold expands on associations to special subselects', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('unfold expand, structured field with alias', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { author { name, address as BUBU } }` const qx = cds.ql`SELECT from bookshop.Books as Books { ( SELECT - author.name, author.address_street as BUBU_street, author.address_city as BUBU_city - from bookshop.Authors as author where Books.author_ID = author.ID) as author + $a.name, $a.address_street as BUBU_street, $a.address_city as BUBU_city + from bookshop.Authors as $a where Books.author_ID = $a.ID) as author }` const res = cqn4sql(q) expect(res.SELECT.columns[0].SELECT).to.have.property('expand').that.equals(true) @@ -550,15 +535,15 @@ describe('Unfold expands on associations to special subselects', () => { }) it('unfold expand, *', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { author { * } }` const qx = cds.ql`SELECT from bookshop.Books as Books { ( SELECT - author.createdAt, author.createdBy, author.modifiedAt, author.modifiedBy, - author.ID, author.name, author.dateOfBirth, author.dateOfDeath, author.placeOfBirth, author.placeOfDeath, - author.address_street, author.address_city - from bookshop.Authors as author where Books.author_ID = author.ID) as author + $a.createdAt, $a.createdBy, $a.modifiedAt, $a.modifiedBy, + $a.ID, $a.name, $a.dateOfBirth, $a.dateOfDeath, $a.placeOfBirth, $a.placeOfDeath, + $a.address_street, $a.address_city + from bookshop.Authors as $a where Books.author_ID = $a.ID) as author }` const res = cqn4sql(q) expect(res.SELECT.columns[0].SELECT).to.have.property('expand').that.equals(true) @@ -568,11 +553,11 @@ describe('Unfold expands on associations to special subselects', () => { // explicit alias for struc name is also used as table alias for subquery it('unfold expand, with association alias', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { author as a { name } }` - const qx = cds.ql`SELECT from bookshop.Books as Books { - (SELECT a.name from bookshop.Authors as a where Books.author_ID = a.ID) as a + const qx = cds.ql`SELECT from bookshop.Books as Books{ + (SELECT $a.name from bookshop.Authors as $a where Books.author_ID = $a.ID) as a }` const res = cqn4sql(q) expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) @@ -581,26 +566,26 @@ describe('Unfold expands on associations to special subselects', () => { // if the provided alias needs to be renamed when used as table alias, the alias for the element // must not change it('unfold expand, with duplicate association alias', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { author as books { name } }` const qx = cds.ql`SELECT from bookshop.Books as Books { - (SELECT books2.name from bookshop.Authors as books2 where Books.author_ID = books2.ID) as books + (SELECT $b.name from bookshop.Authors as $b where Books.author_ID = $b.ID) as books }` const res = cqn4sql(q) expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('unfold expand, two expands', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { author as a1 { name }, author as a2 { name } }` const qx = cds.ql`SELECT from bookshop.Books as Books { - (SELECT a1.name from bookshop.Authors as a1 - where Books.author_ID = a1.ID) as a1, - (SELECT a2.name from bookshop.Authors as a2 - where Books.author_ID = a2.ID) as a2 + (SELECT $a.name from bookshop.Authors as $a + where Books.author_ID = $a.ID) as a1, + (SELECT $a2.name from bookshop.Authors as $a2 + where Books.author_ID = $a2.ID) as a2 }` const res = cqn4sql(q) expect(res.SELECT.columns[0].SELECT).to.have.property('expand').that.equals(true) @@ -617,11 +602,11 @@ describe('Unfold expands on associations to special subselects', () => { genre.name } }` - const qx = cds.ql`SELECT from bookshop.Authors as Authors { - (SELECT books.title, genre.name AS genre_name - FROM bookshop.Books AS books - LEFT JOIN bookshop.Genres AS genre ON genre.ID = books.genre_ID - WHERE Authors.ID = books.author_ID + const qx = cds.ql`SELECT from bookshop.Authors as $A { + (SELECT $b.title, genre.name AS genre_name + FROM bookshop.Books AS $b + LEFT JOIN bookshop.Genres AS genre ON genre.ID = $b.genre_ID + WHERE $A.ID = $b.author_ID ) as books }` const res = cqn4sql(q) @@ -631,7 +616,7 @@ describe('Unfold expands on associations to special subselects', () => { }) it('unfold expand, expand after association', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { title, author.books { title } }` @@ -639,7 +624,7 @@ describe('Unfold expands on associations to special subselects', () => { left outer join bookshop.Authors as author on author.ID = Books.author_ID { Books.title, - (SELECT author_books.title from bookshop.Books as author_books where author.ID = author_books.author_ID) as author_books + (SELECT $a.title from bookshop.Books as $a where author.ID = $a.author_ID) as author_books }` const res = cqn4sql(q) expect(res.SELECT.columns[1].SELECT).to.have.property('expand').that.equals(true) @@ -649,7 +634,7 @@ describe('Unfold expands on associations to special subselects', () => { // TODO (SMW) new test it('unfold expand, expand after association (2)', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { title, author.name, author.books { title } @@ -659,7 +644,7 @@ describe('Unfold expands on associations to special subselects', () => { { Books.title, author.name as author_name, - (SELECT author_books.title from bookshop.Books as author_books where author.ID = author_books.author_ID) as author_books + (SELECT $a.title from bookshop.Books as $a where author.ID = $a.author_ID) as author_books }` const res = cqn4sql(q) expect(res.SELECT.columns[2].SELECT).to.have.property('expand').that.equals(true) @@ -668,7 +653,7 @@ describe('Unfold expands on associations to special subselects', () => { }) it('unfold expand, expand after association (3) with infix filter on last step', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { title, author.name, author.books[title = 'foo'] { title } @@ -678,7 +663,7 @@ describe('Unfold expands on associations to special subselects', () => { { Books.title, author.name as author_name, - (SELECT author_books.title from bookshop.Books as author_books where author.ID = author_books.author_ID and author_books.title = 'foo') as author_books + (SELECT $a.title from bookshop.Books as $a where author.ID = $a.author_ID and $a.title = 'foo') as author_books }` const res = cqn4sql(q) expect(res.SELECT.columns[2].SELECT).to.have.property('expand').that.equals(true) @@ -687,7 +672,7 @@ describe('Unfold expands on associations to special subselects', () => { }) it('unfold expand, expand after association (4) with infix filter in intermediate step', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { title, author.name, Books.author.books.genre[name = 'foo'] { name } @@ -698,7 +683,7 @@ describe('Unfold expands on associations to special subselects', () => { { Books.title, author.name as author_name, - (SELECT author_books_genre.name from bookshop.Genres as author_books_genre where books2.genre_ID = author_books_genre.ID and author_books_genre.name = 'foo') as author_books_genre + (SELECT $a.name from bookshop.Genres as $a where books2.genre_ID = $a.ID and $a.name = 'foo') as author_books_genre }` const res = cqn4sql(q) expect(res.SELECT.columns[2].SELECT).to.have.property('expand').that.equals(true) @@ -706,7 +691,7 @@ describe('Unfold expands on associations to special subselects', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('unfold expand, expand after association (5) with infix filter on first step', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { title, author[name='Sanderson'].name, author[name='Sanderson'].books.genre { name } @@ -717,7 +702,7 @@ describe('Unfold expands on associations to special subselects', () => { { Books.title, author.name as author_name, - (SELECT author_books_genre.name from bookshop.Genres as author_books_genre where books2.genre_ID = author_books_genre.ID) as author_books_genre + (SELECT $a.name from bookshop.Genres as $a where books2.genre_ID = $a.ID) as author_books_genre }` const res = cqn4sql(q) expect(res.SELECT.columns[2].SELECT).to.have.property('expand').that.equals(true) @@ -730,14 +715,14 @@ describe('Unfold expands on associations to special subselects', () => { foo, toAuthor.books { title } }` - const qx = cds.ql`SELECT from bookshop.AssocAsKey as AssocAsKey - left outer join bookshop.Authors as toAuthor on toAuthor.ID = AssocAsKey.toAuthor_ID + const qx = cds.ql`SELECT from bookshop.AssocAsKey as $A + left outer join bookshop.Authors as toAuthor on toAuthor.ID = $A.toAuthor_ID { - AssocAsKey.foo, + $A.foo, ( - SELECT toAuthor_books.title - from bookshop.Books as toAuthor_books - where toAuthor.ID = toAuthor_books.author_ID + SELECT $t.title + from bookshop.Books as $t + where toAuthor.ID = $t.author_ID ) as toAuthor_books }` const res = cqn4sql(q) @@ -749,12 +734,12 @@ describe('Unfold expands on associations to special subselects', () => { // TODO clarify if it would be okay to only forbid addressing to many expands it('unfold expand // reference in order by is NOT referring to expand column', () => { const input = cds.ql`SELECT from bookshop.Books.twin { author { name } } order by author.name asc` - let qx = cds.ql`SELECT from bookshop.Books.twin as twin - left outer join bookshop.Authors as author on author.ID = twin.author_ID + let qx = cds.ql`SELECT from bookshop.Books.twin as $t + left outer join bookshop.Authors as author on author.ID = $t.author_ID { ( - select author2.name from bookshop.Authors as author2 - where twin.author_ID = author2.ID + select $a.name from bookshop.Authors as $a + where $t.author_ID = $a.ID ) as author } order by author.name asc ` @@ -767,12 +752,12 @@ describe('Unfold expands on associations to special subselects', () => { ID, one.two.three.toSelf { ID } }` - const qx = cds.ql`SELECT from bookshop.DeepRecursiveAssoc as DeepRecursiveAssoc { - DeepRecursiveAssoc.ID, + const qx = cds.ql`SELECT from bookshop.DeepRecursiveAssoc as $D { + $D.ID, ( - SELECT one_two_three_toSelf.ID - from bookshop.DeepRecursiveAssoc as one_two_three_toSelf - where DeepRecursiveAssoc.one_two_three_toSelf_ID = one_two_three_toSelf.ID + SELECT $o.ID + from bookshop.DeepRecursiveAssoc as $o + where $D.one_two_three_toSelf_ID = $o.ID ) as one_two_three_toSelf }` const res = cqn4sql(q) @@ -785,15 +770,15 @@ describe('Unfold expands on associations to special subselects', () => { ID, one.two.three.toSelf.one.two.three.toSelf { ID } }` - const qx = cds.ql`SELECT from bookshop.DeepRecursiveAssoc as DeepRecursiveAssoc + const qx = cds.ql`SELECT from bookshop.DeepRecursiveAssoc as $D left outer join bookshop.DeepRecursiveAssoc as toSelf on - toSelf.ID = DeepRecursiveAssoc.one_two_three_toSelf_ID + toSelf.ID = $D.one_two_three_toSelf_ID { - DeepRecursiveAssoc.ID, + $D.ID, ( - SELECT one_two_three_toSelf_one_two_three_toSelf.ID - from bookshop.DeepRecursiveAssoc as one_two_three_toSelf_one_two_three_toSelf - where toSelf.one_two_three_toSelf_ID = one_two_three_toSelf_one_two_three_toSelf.ID + SELECT $o.ID + from bookshop.DeepRecursiveAssoc as $o + where toSelf.one_two_three_toSelf_ID = $o.ID ) as one_two_three_toSelf_one_two_three_toSelf }` const res = cqn4sql(q) @@ -812,15 +797,15 @@ describe('Unfold expands on associations to special subselects', () => { } } }` - const qx = cds.ql`SELECT from bookshop.Books as Books { + const qx = cds.ql`SELECT from bookshop.Books as $B { ( SELECT ( SELECT - ( SELECT genre.name - FROM bookshop.Genres as genre WHERE books2.genre_ID = genre.ID + ( SELECT $g.name + FROM bookshop.Genres as $g WHERE $b2.genre_ID = $g.ID ) as genre - FROM bookshop.Books AS books2 WHERE author.ID = books2.author_ID + FROM bookshop.Books AS $b2 WHERE $a.ID = $b2.author_ID ) as books - FROM bookshop.Authors as author WHERE Books.author_ID = author.ID + FROM bookshop.Authors as $a WHERE $B.author_ID = $a.ID ) as author }` const res = cqn4sql(q) @@ -844,11 +829,11 @@ describe('Unfold expands on associations to special subselects', () => { name, books { title } }` - const qx = cds.ql`SELECT from bookshop.Authors as author { - author.name, - (SELECT books2.title from bookshop.Books as books2 - where author.ID = books2.author_ID) as books - } where exists (SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID)` + const qx = cds.ql`SELECT from bookshop.Authors as $a { + $a.name, + (SELECT $b2.title from bookshop.Books as $b2 + where $a.ID = $b2.author_ID) as books + } where exists (SELECT 1 from bookshop.Books as $B where $B.author_ID = $a.ID)` const res = cqn4sql(q) expect(res.SELECT.columns[1].SELECT).to.have.property('expand').that.equals(true) expect(res.SELECT.columns[1].SELECT).to.have.property('one').that.equals(false) @@ -859,32 +844,32 @@ describe('Unfold expands on associations to special subselects', () => { name, books { 'first' as first, 'second' as ID, *, 'third' as createdAt, 'last' as last } }` - const qx = cds.ql`SELECT from bookshop.Authors as Authors { - Authors.name, + const qx = cds.ql`SELECT from bookshop.Authors as $A { + $A.name, (SELECT 'first' as first, 'second' as ID, 'third' as createdAt, - books.createdBy, - books.modifiedAt, - books.modifiedBy, - books.anotherText, - books.title, - books.descr, - books.author_ID, - books.coAuthor_ID, - books.genre_ID, - books.stock, - books.price, - books.currency_code, - books.dedication_addressee_ID, - books.dedication_text, - books.dedication_sub_foo, - books.dedication_dedication, - books.coAuthor_ID_unmanaged, + $b.createdBy, + $b.modifiedAt, + $b.modifiedBy, + $b.anotherText, + $b.title, + $b.descr, + $b.author_ID, + $b.coAuthor_ID, + $b.genre_ID, + $b.stock, + $b.price, + $b.currency_code, + $b.dedication_addressee_ID, + $b.dedication_text, + $b.dedication_sub_foo, + $b.dedication_dedication, + $b.coAuthor_ID_unmanaged, 'last' as last - from bookshop.Books as books - where Authors.ID = books.author_ID + from bookshop.Books as $b + where $A.ID = $b.author_ID ) as books }` const res = cqn4sql(q) @@ -903,23 +888,23 @@ describe('Unfold expands on associations to special subselects', () => { } } }` - const expected = cds.ql`SELECT from bookshop.WorklistItems as WorklistItems { - WorklistItems.ID, + const expected = cds.ql`SELECT from bookshop.WorklistItems as $W { + $W.ID, ( - SELECT from bookshop.WorklistItem_ReleaseChecks as releaseChecks { - releaseChecks.ID, + SELECT from bookshop.WorklistItem_ReleaseChecks as $r { + $r.ID, ( - SELECT from bookshop.QualityDeviations as detailsDeviations { - detailsDeviations.ID - } where detailsDeviations.material_ID = releaseChecks.parent_releaseDecisionTrigger_batch_material_ID + SELECT from bookshop.QualityDeviations as $d { + $d.ID + } where $d.material_ID = $r.parent_releaseDecisionTrigger_batch_material_ID and ( - detailsDeviations.batch_ID = '*' - or detailsDeviations.batch_ID = releaseChecks.parent_releaseDecisionTrigger_batch_ID + $d.batch_ID = '*' + or $d.batch_ID = $r.parent_releaseDecisionTrigger_batch_ID ) - and detailsDeviations.snapshotHash = releaseChecks.snapshotHash + and $d.snapshotHash = $r.snapshotHash ) as detailsDeviations - } where releaseChecks.parent_ID = WorklistItems.ID - and releaseChecks.parent_snapshotHash = WorklistItems.snapshotHash + } where $r.parent_ID = $W.ID + and $r.parent_snapshotHash = $W.snapshotHash ) as releaseChecks } ` @@ -927,7 +912,7 @@ describe('Unfold expands on associations to special subselects', () => { }) it('ignores expands which target ”@cds.persistence.skip”', () => { - const q = cds.ql`SELECT from bookshop.NotSkipped { + const q = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped { ID, skipped { text } }` const qx = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped { @@ -937,7 +922,7 @@ describe('Unfold expands on associations to special subselects', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('ignores expand if assoc in path expression has target ”@cds.persistence.skip”', () => { - const q = cds.ql`SELECT from bookshop.NotSkipped { + const q = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped { ID, skipped.notSkipped { text } }` const qx = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped { @@ -948,7 +933,7 @@ describe('Unfold expands on associations to special subselects', () => { }) describe('anonymous expand', () => { it('scalar elements', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, { title, @@ -966,7 +951,7 @@ describe('Unfold expands on associations to special subselects', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('scalar elements, structure with renaming and association', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, { title, @@ -986,7 +971,7 @@ describe('Unfold expands on associations to special subselects', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('mixed with inline', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, { dedication.{ @@ -1005,7 +990,7 @@ describe('Unfold expands on associations to special subselects', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('join relevant association', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, { author.name @@ -1030,11 +1015,11 @@ describe('Unfold expands on associations to special subselects', () => { it('assoc comparison needs to be expanded in on condition calculation', () => { const query = cqn4sql(cds.ql`SELECT from a2j.Foo { ID, buz { foo } }`, model) const expected = cds.ql` - SELECT from a2j.Foo as Foo { - Foo.ID, + SELECT from a2j.Foo as $F { + $F.ID, ( - SELECT buz.foo_ID from a2j.Buz as buz - where (buz.bar_ID = Foo.bar_ID AND buz.bar_foo_ID = Foo.bar_foo_ID) and buz.foo_ID = Foo.ID + SELECT $b.foo_ID from a2j.Buz as $b + where ($b.bar_ID = $F.bar_ID AND $b.bar_foo_ID = $F.bar_foo_ID) and $b.foo_ID = $F.ID ) as buz }` expect(JSON.parse(JSON.stringify(query))).to.eql(expected) @@ -1042,11 +1027,11 @@ describe('Unfold expands on associations to special subselects', () => { it('unmanaged association path traversal in on condition needs to be flattened', () => { const query = cqn4sql(cds.ql`SELECT from a2j.Foo { ID, buzUnmanaged { foo } }`, model) const expected = cds.ql` - SELECT from a2j.Foo as Foo { - Foo.ID, + SELECT from a2j.Foo as $F { + $F.ID, ( - SELECT buzUnmanaged.foo_ID from a2j.Buz as buzUnmanaged - where buzUnmanaged.bar_foo_ID = Foo.bar_foo_ID and buzUnmanaged.bar_ID = Foo.bar_ID and buzUnmanaged.foo_ID = Foo.ID + SELECT $b.foo_ID from a2j.Buz as $b + where $b.bar_foo_ID = $F.bar_foo_ID and $b.bar_ID = $F.bar_ID and $b.foo_ID = $F.ID ) as buzUnmanaged }` expect(JSON.parse(JSON.stringify(query))).to.eql(expected) @@ -1072,26 +1057,41 @@ describe('Unfold expands on associations to special subselects', () => { ` let transformed = cqn4sql(q, cds.compile.for.nodejs(JSON.parse(JSON.stringify(model)))) expect(JSON.parse(JSON.stringify(transformed))).to.deep.eql(cds.ql` - SELECT from Collaborations as Collaborations { - Collaborations.id, + SELECT from Collaborations as $C { + $C.id, ( - SELECT from CollaborationLeads as leads { - leads.id - } where ( Collaborations.id = leads.collaboration_id ) and leads.isLead = true + SELECT from CollaborationLeads as $l { + $l.id + } where ( $C.id = $l.collaboration_id ) and $l.isLead = true ) as leads, ( - SELECT from SubCollaborations as subCollaborations { - subCollaborations.id, + SELECT from SubCollaborations as $s { + $s.id, ( - SELECT from SubCollaborationAssignments as leads2 { - leads2.id - } where ( subCollaborations.id = leads2.subCollaboration_id ) and leads2.isLead = true + SELECT from SubCollaborationAssignments as $l2 { + $l2.id + } where ( $s.id = $l2.subCollaboration_id ) and $l2.isLead = true ) as leads - } where Collaborations.id = subCollaborations.collaboration_id + } where $C.id = $s.collaboration_id ) as subCollaborations } `) }) + + it('assign unique subquery alias if implicit alias would be ambiguous', () => { + const q = cds.ql`SELECT from bookshop.Item as $t { + Item { + ID + } + }` + const expected = cds.ql`SELECT from bookshop.Item as $t { + ( + SELECT $I.ID from bookshop.Item as $I + where $t.Item_ID = $I.ID + ) as Item + }` + expect(JSON.parse(JSON.stringify(cqn4sql(q, model)))).to.eql(expected) + }) }) describe('Expands with aggregations are special', () => { @@ -1101,7 +1101,7 @@ describe('Expands with aggregations are special', () => { }) it('simple aggregation', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, Books.author { name } } group by author.name` @@ -1116,7 +1116,7 @@ describe('Expands with aggregations are special', () => { }) it('aggregation with mulitple path steps', () => { - const q = cds.ql`SELECT from bookshop.Intermediate { + const q = cds.ql`SELECT from bookshop.Intermediate as Intermediate { ID, toAssocWithStructuredKey { toStructuredKey { second } } } group by toAssocWithStructuredKey.toStructuredKey.second` @@ -1155,7 +1155,7 @@ describe('Expands with aggregations are special', () => { }) it('wildcard expand vanishes for aggregations', () => { - const q = cds.ql`SELECT from bookshop.TestPublisher { + const q = cds.ql`SELECT from bookshop.TestPublisher as TestPublisher { ID, texts { publisher {*} } } group by ID, publisher.structuredKey_ID, publisher.title` @@ -1189,7 +1189,7 @@ describe('Expands with aggregations are special', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('optimized foreign key access', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, Books.author { name, ID } } group by author.name, author.ID` @@ -1203,7 +1203,7 @@ describe('Expands with aggregations are special', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('foreign key access renamed', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, Books.author { name, ID as foo } } group by author.name, author.ID` @@ -1217,7 +1217,7 @@ describe('Expands with aggregations are special', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('non optimized foreign key access with filters', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, Books.author[ID = 201] { name, ID } } group by author[ID = 201].name, author[ID = 201].ID` @@ -1233,7 +1233,7 @@ describe('Expands with aggregations are special', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('expand path with filter must be an exact match in group by', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { Books.ID, author[name='King'] { name } } group by author[name='King'].name` @@ -1249,7 +1249,7 @@ describe('Expands with aggregations are special', () => { }) it('with multiple expands', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, Books.author { name }, genre { name } @@ -1269,7 +1269,7 @@ describe('Expands with aggregations are special', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('with nested expands', () => { - const q = cds.ql`SELECT from bookshop.Genres { + const q = cds.ql`SELECT from bookshop.Genres as Genres { ID, Genres.parent { parent { name } }, } group by parent.parent.name` @@ -1291,7 +1291,7 @@ describe('Expands with aggregations are special', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('with nested expands and non-nested sibling', () => { - const q = cds.ql`SELECT from bookshop.Genres { + const q = cds.ql`SELECT from bookshop.Genres as Genres { ID, Genres.parent { parent { name }, name }, } group by parent.parent.name, parent.name` @@ -1316,7 +1316,7 @@ describe('Expands with aggregations are special', () => { // negative tests it('simple path not part of group by', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, Books.author { name, ID } } group by author.name` @@ -1324,7 +1324,7 @@ describe('Expands with aggregations are special', () => { expect(() => cqn4sql(q, model)).to.throw(/The expanded column "author.ID" must be part of the group by clause/) }) it('nested path not part of group by', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, Books.author { books {title}, ID } } group by author.ID` @@ -1334,7 +1334,7 @@ describe('Expands with aggregations are special', () => { ) }) it('deeply nested path not part of group by', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID, Books.author { books { author { name } } , ID } } group by author.ID` @@ -1345,7 +1345,7 @@ describe('Expands with aggregations are special', () => { }) it('expand path with filter must be an exact match in group by', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { Books.ID, author[name='King'] { name } } group by author.name` @@ -1355,7 +1355,7 @@ describe('Expands with aggregations are special', () => { ) }) it('expand path with filter must be an exact match in group by (2)', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { Books.ID, author { name } } group by author[name='King'].name` @@ -1373,7 +1373,7 @@ describe('expand on structure part II', () => { }) it('simple structural expansion', () => { - let expandQuery = cds.ql`select from Employee { + let expandQuery = cds.ql`select from Employee as Employee { office { floor, room @@ -1387,7 +1387,7 @@ describe('expand on structure part II', () => { expect(cqn4sql(expandQuery, model)).to.eql(expected) }) it('structural expansion with path expression', () => { - let expandQuery = cds.ql`select from Employee { + let expandQuery = cds.ql`select from Employee as Employee { office { floor, building.name @@ -1403,7 +1403,7 @@ describe('expand on structure part II', () => { }) it('deep expand', () => { - let expandQuery = cds.ql`select from Employee { + let expandQuery = cds.ql`select from Employee as Employee { office { floor, address { @@ -1432,13 +1432,13 @@ describe('expand on structure part II', () => { descr } } excluding { office_floor, office_address_country, office_building, office_room, office_building_id, office_address_city, office_building_id, office_address_street, office_address_country_code, office_address_country_code, office_furniture_chairs,office_furniture_desks }` - let expected = cds.ql`SELECT from Employee as Employee { - Employee.id, - Employee.name, - Employee.job, - Employee.department_id, - (SELECT department.id, department.name from Department as department where Employee.department_id = department.id) as department, - (SELECT assets.id, assets.descr from Assets as assets where Employee.id = assets.owner_id) as assets + let expected = cds.ql`SELECT from Employee as $E { + $E.id, + $E.name, + $E.job, + $E.department_id, + (SELECT $d.id, $d.name from Department as $d where $E.department_id = $d.id) as department, + (SELECT $a.id, $a.descr from Assets as $a where $E.id = $a.owner_id) as assets }` expect( JSON.parse(JSON.stringify(cqn4sql(expandQuery, cds.compile.for.nodejs(JSON.parse(JSON.stringify(model)))))), @@ -1457,18 +1457,20 @@ describe('expand on structure part II', () => { descr } } excluding { office }` - let expected = cds.ql`SELECT from Employee as Employee { - Employee.id, - Employee.name, - Employee.job, - (SELECT department.id, department.name from Department as department where Employee.department_id = department.id) as department, - (SELECT assets.id, assets.descr from Assets as assets where Employee.id = assets.owner_id) as assets + let expected = cds.ql`SELECT from Employee as $E { + $E.id, + $E.name, + $E.job, + (SELECT $d.id, $d.name from Department as $d where $E.department_id = $d.id) as department, + (SELECT $a.id, $a.descr from Assets as $a where $E.id = $a.owner_id) as assets }` expect(JSON.parse(JSON.stringify(cqn4sql(expandQuery, model)))).to.eql(expected) }) + // Implicit alias of nested expand subquery is the first letter + // of the column alias it('structured expand with deep assoc expand', () => { - let expandQuery = cds.ql`select from Employee { + let expandQuery = cds.ql`select from Employee as Employee { office { floor, address { @@ -1483,15 +1485,15 @@ describe('expand on structure part II', () => { Employee.office_address_city, Employee.office_address_street, ( - SELECT office_address_country.code from Country as office_address_country - where Employee.office_address_country_code = office_address_country.code + SELECT $o.code from Country as $o + where Employee.office_address_country_code = $o.code ) as office_address_country }` // expand subqueries have special non-enumerable props -> ignore them expect(JSON.parse(JSON.stringify(cqn4sql(expandQuery, model)))).to.eql(expected) }) it('deep, structured expand', () => { - let expandQuery = cds.ql`select from Employee { + let expandQuery = cds.ql`select from Employee as Employee { office { floor, address { @@ -1500,7 +1502,7 @@ describe('expand on structure part II', () => { } } }` - let expected = cds.ql`select from Employee as Employee{ + let expected = cds.ql`select from Employee as Employee { Employee.office_floor, Employee.office_address_city, Employee.office_address_street, @@ -1508,7 +1510,7 @@ describe('expand on structure part II', () => { expect(cqn4sql(expandQuery, model)).to.eql(expected) }) it('deep expand on assoc within structure expand', () => { - let expandQuery = cds.ql`select from Employee { + let expandQuery = cds.ql`select from Employee as Employee { office { floor, building { @@ -1519,8 +1521,8 @@ describe('expand on structure part II', () => { let expected = cds.ql`select from Employee as Employee { Employee.office_floor, ( - select office_building.id from Building as office_building - where Employee.office_building_id = office_building.id + select $o.id from Building as $o + where Employee.office_building_id = $o.id ) as office_building }` // expand subqueries have special non-enumerable props -> ignore them @@ -1528,10 +1530,10 @@ describe('expand on structure part II', () => { }) it('wildcard expand toplevel', () => { - let expandQuery = cds.ql`select from EmployeeNoUnmanaged { + let expandQuery = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office { * } }` - let absolutePaths = cds.ql`select from EmployeeNoUnmanaged { + let absolutePaths = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.floor, office.room, office.building, @@ -1554,7 +1556,7 @@ describe('expand on structure part II', () => { expect(wildcard).to.eql(absolute).to.eql(expected) }) it('wildcard on expand deep', () => { - let expandQuery = cds.ql`select from EmployeeNoUnmanaged { + let expandQuery = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office { address {*} } }` let expected = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { @@ -1568,7 +1570,7 @@ describe('expand on structure part II', () => { it('smart wildcard - assoc overwrite after *', () => { // office.address.city replaces office.floor - let expandQuery = cds.ql`select from EmployeeNoUnmanaged { + let expandQuery = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office { *, furniture as building, address.city as floor, building.id as room } }` let expected = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { @@ -1588,7 +1590,7 @@ describe('expand on structure part II', () => { it('smart wildcard - structure overwritten by assoc before *', () => { // intermediate structures are overwritten - let expandQuery = cds.ql`select from EmployeeNoUnmanaged { + let expandQuery = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{ building as furniture, * } }` let expected = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { @@ -1604,7 +1606,7 @@ describe('expand on structure part II', () => { }) it('smart wildcard - structure overwritten by join relevant assoc before *', () => { // intermediate structures are overwritten - let expandQuery = cds.ql`select from EmployeeNoUnmanaged { + let expandQuery = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office { building[name='mega tower'].name as furniture, * } }` let expected = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged @@ -1622,7 +1624,7 @@ describe('expand on structure part II', () => { }) it('wildcard - no overwrite but additional cols', () => { // intermediate structures are overwritten - let expandQuery = cds.ql`select from EmployeeNoUnmanaged { + let expandQuery = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office { *, 'foo' as last } }` let expected = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged @@ -1641,7 +1643,7 @@ describe('expand on structure part II', () => { }) it('assigning alias within expand only influences name of element, prefix still appended', () => { // intermediate structures are overwritten - let expandQuery = cds.ql`select from EmployeeNoUnmanaged { + let expandQuery = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office { floor as x } }` let expected = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { @@ -1651,7 +1653,7 @@ describe('expand on structure part II', () => { }) it('smart wildcard - structured overwrite before *', () => { // intermediate structures are overwritten - let expandQuery = cds.ql`select from EmployeeNoUnmanaged { + let expandQuery = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office { 'first' as furniture, 'second' as building, * } }` let expected = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { @@ -1667,7 +1669,7 @@ describe('expand on structure part II', () => { }) it('smart wildcard - structured overwrite after *', () => { // intermediate structures are overwritten - let expandQuery = cds.ql`select from EmployeeNoUnmanaged { + let expandQuery = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office {*, 'third' as building, 'fourth' as address } }` let expected = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { @@ -1683,7 +1685,7 @@ describe('expand on structure part II', () => { it('wildcard expansion - exclude association', () => { // intermediate structures are overwritten - let expandQuery = cds.ql`select from EmployeeNoUnmanaged { + let expandQuery = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office {*} excluding { building, address } }` let expected = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { diff --git a/db-service/test/cqn4sql/flattening.test.js b/db-service/test/cqn4sql/flattening.test.js index 6346f6048..305cda21d 100644 --- a/db-service/test/cqn4sql/flattening.test.js +++ b/db-service/test/cqn4sql/flattening.test.js @@ -14,7 +14,7 @@ describe('Flattening', () => { describe('in columns', () => { it('unfolds structure', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Bar { + cds.ql`SELECT from bookshop.Bar as Bar { ID, structure }`, @@ -44,7 +44,7 @@ describe('Flattening', () => { it('unfolds structure also with alias', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Bar { + cds.ql`SELECT from bookshop.Bar as Bar { structure as ding }`, model, @@ -57,7 +57,7 @@ describe('Flattening', () => { it('unfolds structure repeatedly if properly aliased', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Bar { + cds.ql`SELECT from bookshop.Bar as Bar { structure as ding, Bar.structure as bing }`, @@ -73,7 +73,7 @@ describe('Flattening', () => { it('unfolds nested structure', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Bar { + cds.ql`SELECT from bookshop.Bar as Bar { nested }`, model, @@ -85,9 +85,8 @@ describe('Flattening', () => { }`) }) // unmanaged ... - it('ignores unmanaged association in SELECT clause (has no value)', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { author, coAuthorUnmanaged }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { author, coAuthorUnmanaged }`, model) expect(query).to.deep.eql(cds.ql`SELECT from bookshop.Books as Books { Books.author_ID }`) }) @@ -137,7 +136,7 @@ describe('Flattening', () => { it('unfolds managed associations in SELECT clause', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, author, coAuthor, @@ -155,7 +154,7 @@ describe('Flattening', () => { it('unfolds managed associations in SELECT clause with foreign keys', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, author, author_ID, @@ -192,7 +191,7 @@ describe('Flattening', () => { it('unfolds managed associations in SELECT clause also with alias', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, author as person, Books.genre as topic @@ -326,7 +325,7 @@ describe('Flattening', () => { // TODO move out it('does not transform queries with multiple query sources, but just returns the inferred query', () => { - const query = cds.ql`SELECT from bookshop.Books, bookshop.Authors {Books.ID as bid, Authors.ID as aid}` + const query = cds.ql`SELECT from bookshop.Books as Books, bookshop.Authors as Authors {Books.ID as bid, Authors.ID as aid}` expect(cqn4sql(query, model)).to.deep.equal(_inferred(query, model)) }) @@ -375,8 +374,8 @@ describe('Flattening', () => { describe('in where', () => { it('unfolds structure in subquery', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID } WHERE exists ( - SELECT address from bookshop.Authors where ID > Books.ID + cds.ql`SELECT from bookshop.Books as Books { ID } WHERE exists ( + SELECT address from bookshop.Authors as Authors where ID > Books.ID )`, model, ) @@ -441,9 +440,9 @@ describe('Flattening', () => { it('unfolds structure in value subquery (result is invalid SQL)', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, - (SELECT from bookshop.Person { address }) as foo + (SELECT from bookshop.Person as Person { address }) as foo }`, model, ) @@ -455,8 +454,8 @@ describe('Flattening', () => { it('unfolds structure in value subquery (result is invalid SQL), access outer table alias in inner query', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { - (SELECT address from bookshop.Authors where ID > Books.ID) as authorColumn, + cds.ql`SELECT from bookshop.Books as Books { + (SELECT address from bookshop.Authors as Authors where ID > Books.ID) as authorColumn, (SELECT from bookshop.Genres as G { (SELECT address from bookshop.Authors as genreAuthor where ID > Books.ID and G.ID = 42) as AuthorInG, @@ -480,15 +479,15 @@ describe('Flattening', () => { }`, model, ) - expect(JSON.parse(JSON.stringify(query))).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { - Books.ID, - (SELECT from bookshop.Books as Books2 { Books2.author_ID }) as foo + expect(JSON.parse(JSON.stringify(query))).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { + $B.ID, + (SELECT from bookshop.Books as $B2 { $B2.author_ID }) as foo }`) }) it('unfolds managed association in value subquery (result is invalid SQL)', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, (SELECT from bookshop.AssocMaze1 as AM { a_struc as a }) as foo }`, @@ -505,7 +504,7 @@ describe('Flattening', () => { it('unfolds managed association in EXISTS subquery', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors { ID } WHERE exists ( + cds.ql`SELECT from bookshop.Authors as Authors { ID } WHERE exists ( SELECT author from bookshop.Books as Books where Books.ID > Authors.ID )`, model, @@ -518,7 +517,7 @@ describe('Flattening', () => { it('unfolds managed association in FROM subquery', () => { let query = cqn4sql( - cds.ql`SELECT from (select from bookshop.Books { author, coAuthor as co}) as Q { + cds.ql`SELECT from (select from bookshop.Books as Books { author, coAuthor as co}) as Q { author, co }`, @@ -536,13 +535,13 @@ describe('Flattening', () => { describe('in order by', () => { it('unfolds struct field with a single element in ORDER BY clause', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Bar { stock } ORDER BY struct1`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Bar as Bar { stock } ORDER BY struct1`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Bar as Bar { Bar.stock } ORDER BY Bar.struct1_foo `) }) it('unfolds nested struct field with a single leaf element in ORDER BY clause', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Bar { stock } ORDER BY nested1`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Bar as Bar { stock } ORDER BY nested1`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Bar as Bar { Bar.stock } ORDER BY Bar.nested1_foo_x `) @@ -593,7 +592,7 @@ describe('Flattening', () => { it('xy unfolds structured access to a single element in ORDER BY clause', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Bar { structure as out } ORDER BY out.foo, out.baz, Bar.nested.foo, Bar.nested.bar.a, Bar.nested.bar.b`, + cds.ql`SELECT from bookshop.Bar as Bar { structure as out } ORDER BY out.foo, out.baz, Bar.nested.foo, Bar.nested.bar.a, Bar.nested.bar.b`, model, ) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Bar as Bar { @@ -610,7 +609,7 @@ describe('Flattening', () => { it('unfolds structured access to a single element in ORDER BY clause', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Bar { structure as out } ORDER BY out.foo, out.baz, Bar.nested.foo, Bar.nested.bar.a, Bar.nested.bar.b`, + cds.ql`SELECT from bookshop.Bar as Bar { structure as out } ORDER BY out.foo, out.baz, Bar.nested.foo, Bar.nested.bar.a, Bar.nested.bar.b`, model, ) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Bar as Bar { @@ -638,7 +637,7 @@ describe('Flattening', () => { }) it('unfolds managed association with one FK in ORDER BY clause', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, author, coAuthor as co } + cds.ql`SELECT from bookshop.Books as Books { ID, author, coAuthor as co } order by Books.author, co`, model, ) @@ -653,7 +652,7 @@ describe('Flattening', () => { }) it('same as above but navigation to foreign key in order by', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, author, coAuthor as co @@ -691,7 +690,7 @@ describe('Flattening', () => { ) }) it('ignores unmanaged associations in ORDER BY clause', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } ORDER BY ID, coAuthorUnmanaged`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } ORDER BY ID, coAuthorUnmanaged`, model) expect(query).to.deep.eql(cds.ql`SELECT from bookshop.Books as Books { Books.ID } order by ID`) }) it('rejects unmanaged associations in expressions in ORDER BY clause (1)', () => { @@ -709,7 +708,7 @@ describe('Flattening', () => { describe('in group by', () => { it('unfolds struct field in GROUP BY clause', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Bar { ID } group by Bar.structure, nested`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Bar as Bar { ID } group by Bar.structure, nested`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Bar as Bar { Bar.ID } group by Bar.structure_foo, Bar.structure_baz, @@ -721,7 +720,7 @@ describe('Flattening', () => { it('unfolds managed association in GROUP BY clause', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, author, coAuthor as co } + cds.ql`SELECT from bookshop.Books as Books { ID, author, coAuthor as co } group by author, Books.coAuthor`, model, ) @@ -736,7 +735,7 @@ describe('Flattening', () => { }) it('if only partial foreign key is accessed, only the requested key is flattened', () => { - const q = cds.ql`SELECT from bookshop.Intermediate { + const q = cds.ql`SELECT from bookshop.Intermediate as Intermediate { ID } group by toAssocWithStructuredKey.toStructuredKey.second` @@ -777,28 +776,28 @@ describe('Flattening', () => { ) }) it('rejects struct fields in expressions in GROUP BY clause (1)', () => { - expect(() => cqn4sql(cds.ql`SELECT from bookshop.Bar { ID } GROUP BY 2*nested`, model)).to.throw( + expect(() => cqn4sql(cds.ql`SELECT from bookshop.Bar as Bar { ID } GROUP BY 2*nested`, model)).to.throw( /A structured element can't be used as a value in an expression/, ) }) it('rejects struct fields in expressions in GROUP BY clause (2)', () => { - expect(() => cqn4sql(cds.ql`SELECT from bookshop.Bar { ID } GROUP BY sin(nested)`, model)).to.throw( + expect(() => cqn4sql(cds.ql`SELECT from bookshop.Bar as Bar { ID } GROUP BY sin(nested)`, model)).to.throw( /A structured element can't be used as a value in an expression/, ) }) it('ignores unmanaged associations in GROUP BY clause', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } GROUP BY ID, coAuthorUnmanaged`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } GROUP BY ID, coAuthorUnmanaged`, model) expect(query).to.deep.eql(cds.ql`SELECT from bookshop.Books as Books { Books.ID } GROUP BY Books.ID`) }) it('ignores unmanaged associations in GROUP BY and deletes the clause if it is the only GROUP BY column', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } GROUP BY coAuthorUnmanaged`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } GROUP BY coAuthorUnmanaged`, model) expect(JSON.parse(JSON.stringify(query))).to.deep.eql(cds.ql`SELECT from bookshop.Books as Books { Books.ID }`) }) it('ignores unmanaged associations in ORDER BY and deletes the clause if it is the only ORDER BY column', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } ORDER BY coAuthorUnmanaged`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } ORDER BY coAuthorUnmanaged`, model) expect(JSON.parse(JSON.stringify(query))).to.deep.eql(cds.ql`SELECT from bookshop.Books as Books { Books.ID }`) }) diff --git a/db-service/test/cqn4sql/functions.test.js b/db-service/test/cqn4sql/functions.test.js index 9b9529771..18ae61547 100644 --- a/db-service/test/cqn4sql/functions.test.js +++ b/db-service/test/cqn4sql/functions.test.js @@ -10,15 +10,15 @@ describe('functions', () => { }) describe('general', () => { it('function in filter of expand', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { author[substring(placeOfBirth, 0, 2) = 'DE'] { name } }` const qx = cds.ql`SELECT from bookshop.Books as Books { ( - SELECT author.name - from bookshop.Authors as author - where Books.author_ID = author.ID and - substring(author.placeOfBirth, 0, 2) = 'DE' + SELECT $a.name + from bookshop.Authors as $a + where Books.author_ID = $a.ID and + substring($a.placeOfBirth, 0, 2) = 'DE' ) as author }` @@ -28,19 +28,19 @@ describe('functions', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('function val in func.args must not be expanded to fk comparison', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { 1 } where not exists author[contains(toLower('foo'))]` const qx = cds.ql`SELECT from bookshop.Books as Books { 1 } where not exists ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID and contains(toLower('foo')) + SELECT 1 from bookshop.Authors as $a where $a.ID = Books.author_ID and contains(toLower('foo')) )` const res = cqn4sql(q, model) expect(res).to.deep.equal(qx) }) it('function with dot operator', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { func1(ID, 'bar').func2(author.name, 'foo') as dotOperator } ` const qx = cds.ql` @@ -55,7 +55,7 @@ describe('functions', () => { describe('with named parameters', () => { it('in column', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { getAuthorsName( author => author.name, book => title ) as foo } ` const qx = cds.ql` @@ -67,7 +67,7 @@ describe('functions', () => { expect(res).to.deep.equal(qx) }) it('in infix filter', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { author[ 'King' = getAuthorsName( author => ID ) ].ID as foo } ` const qx = cds.ql` @@ -81,7 +81,7 @@ describe('functions', () => { expect(res).to.deep.equal(qx) }) it('in where', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID } where getAuthorsName( author => author.name ) = 'King'` const qx = cds.ql` @@ -95,7 +95,7 @@ describe('functions', () => { }) it('in order by', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID } order by getAuthorsName( author => author.name )` const qx = cds.ql` @@ -109,7 +109,7 @@ describe('functions', () => { }) it('in group by', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID } group by getAuthorsName( author => author.name )` const qx = cds.ql` @@ -123,7 +123,7 @@ describe('functions', () => { }) it('in having', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID } having getAuthorsName( author => author.name ) = 'King'` const qx = cds.ql` @@ -137,7 +137,7 @@ describe('functions', () => { }) it('in xpr', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { ID } where ('Stephen ' + getAuthorsName( author => author.name )) = 'Stephen King'` const qx = cds.ql` @@ -150,7 +150,7 @@ describe('functions', () => { expect(res).to.deep.equal(qx) }) it('in from', () => { - const q = cds.ql`SELECT from bookshop.Books[getAuthorsName( author => author.ID ) = 1] { + const q = cds.ql`SELECT from bookshop.Books[getAuthorsName( author => author.ID ) = 1] as Books { ID }` const qx = cds.ql` @@ -167,7 +167,7 @@ describe('functions', () => { it('function in filter in order by', () => { let query = { SELECT: { - from: { ref: ['bookshop.Books'] }, + from: { ref: ['bookshop.Books'], as: 'Books'}, columns: [{ ref: ['ID'] }], where: [{ func: 'current_date' }, '=', { val: 'today' }], }, diff --git a/db-service/test/cqn4sql/inline.test.js b/db-service/test/cqn4sql/inline.test.js index 885645add..55abfd8a3 100644 --- a/db-service/test/cqn4sql/inline.test.js +++ b/db-service/test/cqn4sql/inline.test.js @@ -13,13 +13,13 @@ describe('inline', () => { }) it('simple structural inline expansion', () => { - let inlineQuery = cds.ql`select from Employee { + let inlineQuery = cds.ql`select from Employee as Employee { office.{ floor, room } }` - let longVersion = cds.ql`select from Employee { + let longVersion = cds.ql`select from Employee as Employee { office.floor, office.room }` @@ -30,13 +30,13 @@ describe('inline', () => { expect(cqn4sql(inlineQuery, model)).to.eql(cqn4sql(longVersion, model)).to.eql(expected) }) it('structural inline expansion with path expression', () => { - let inlineQuery = cds.ql`select from Employee { + let inlineQuery = cds.ql`select from Employee as Employee { office.{ floor, building.name } }` - let longVersion = cds.ql`select from Employee { + let longVersion = cds.ql`select from Employee as Employee { office.floor, office.building.name }` @@ -50,7 +50,7 @@ describe('inline', () => { expect(cqn4sql(inlineQuery, model)).to.eql(longResult).to.eql(expected) }) it('inline expansion with path expression', () => { - let inlineQuery = cds.ql`select from Employee { + let inlineQuery = cds.ql`select from Employee as Employee { department.{ name } @@ -63,7 +63,7 @@ describe('inline', () => { expect(cqn4sql(inlineQuery, model)).to.eql(expected) }) it('structural inline expansion with path expression and infix filter', () => { - let inlineQuery = cds.ql`select from Department { + let inlineQuery = cds.ql`select from Department as Department { head[job = 'boss'].office.{ floor } @@ -77,7 +77,7 @@ describe('inline', () => { expect(cqn4sql(inlineQuery, model)).to.eql(expected) }) it('structural inline expansion with path expression and infix filter at leaf', () => { - let inlineQuery = cds.ql`select from Department { + let inlineQuery = cds.ql`select from Department as Department { head[job = 'boss'].{ name } @@ -92,7 +92,7 @@ describe('inline', () => { }) it('structural inline expansion back and forth', () => { - let inlineQuery = cds.ql`select from Department { + let inlineQuery = cds.ql`select from Department as Department { head.department.{ costCenter } @@ -108,7 +108,7 @@ describe('inline', () => { }) it('structural inline expansion back and forth', () => { - let inlineQuery = cds.ql`select from Department { + let inlineQuery = cds.ql`select from Department as Department { head.department.{ costCenter } @@ -124,7 +124,7 @@ describe('inline', () => { }) it('mixed with expand', () => { - let queryInlineNotation = cds.ql`select from Employee { + let queryInlineNotation = cds.ql`select from Employee as Employee { office { floor, address.{ @@ -133,7 +133,7 @@ describe('inline', () => { } } }` - let variantWithoutInline = cds.ql`select from Employee { + let variantWithoutInline = cds.ql`select from Employee as Employee { office { floor, address.city, @@ -150,7 +150,7 @@ describe('inline', () => { }) it('deep inline', () => { - let queryInlineNotation = cds.ql`select from Employee { + let queryInlineNotation = cds.ql`select from Employee as Employee { office.{ floor, address.{ @@ -160,7 +160,7 @@ describe('inline', () => { } } }` - let variantWithoutInline = cds.ql`select from Employee { + let variantWithoutInline = cds.ql`select from Employee as Employee { office.floor, office.address.city, office.address.street, @@ -175,7 +175,7 @@ describe('inline', () => { expect(cqn4sql(queryInlineNotation, model)).to.eql(cqn4sql(variantWithoutInline, model)).to.eql(expected) }) it('deep expand in inline', () => { - let queryInlineNotation = cds.ql`select from Employee { + let queryInlineNotation = cds.ql`select from Employee as Employee { office.{ floor, address { @@ -184,14 +184,14 @@ describe('inline', () => { } } }` - let variantWithoutInline = cds.ql`select from Employee { + let variantWithoutInline = cds.ql`select from Employee as Employee { office.floor, office.address { city, street } }` - let expected = cds.ql`select from Employee as Employee{ + let expected = cds.ql`select from Employee as Employee { Employee.office_floor, Employee.office_address_city, Employee.office_address_street, @@ -199,7 +199,7 @@ describe('inline', () => { expect(cqn4sql(queryInlineNotation, model)).to.eql(cqn4sql(variantWithoutInline, model)).to.eql(expected) }) it('deep expand on assoc in inline', () => { - let queryInlineNotation = cds.ql`select from Employee { + let queryInlineNotation = cds.ql`select from Employee as Employee { office.{ floor, building { @@ -207,7 +207,7 @@ describe('inline', () => { } } }` - let variantWithoutInline = cds.ql`select from Employee { + let variantWithoutInline = cds.ql`select from Employee as Employee { office.floor, office.building { id @@ -216,8 +216,8 @@ describe('inline', () => { let expected = cds.ql`select from Employee as Employee { Employee.office_floor, ( - select office_building.id from Building as office_building - where Employee.office_building_id = office_building.id + select $o.id from Building as $o + where Employee.office_building_id = $o.id ) as office_building }` // expand subqueries have special non-enumerable props -> ignore them @@ -227,10 +227,10 @@ describe('inline', () => { }) it('wildcard inline toplevel', () => { - let inlineWildcard = cds.ql`select from EmployeeNoUnmanaged { + let inlineWildcard = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{ * } }` - let inlineExplicit = cds.ql`select from EmployeeNoUnmanaged { + let inlineExplicit = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{ floor, room, @@ -239,7 +239,7 @@ describe('inline', () => { furniture } }` - let absolutePaths = cds.ql`select from EmployeeNoUnmanaged { + let absolutePaths = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.floor, office.room, office.building, @@ -263,10 +263,10 @@ describe('inline', () => { expect(wildcard).to.eql(explicit).to.eql(absolute).to.eql(expected) }) it('wildcard inline deep w/o brackets', () => { - let inline = cds.ql`select from EmployeeNoUnmanaged { + let inline = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{ address.* } }` - let absolutePaths = cds.ql`select from EmployeeNoUnmanaged { + let absolutePaths = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.address.city, office.address.street, office.address.country, @@ -282,10 +282,10 @@ describe('inline', () => { it('smart wildcard - assoc overwrite after *', () => { // office.address.city replaces office.floor - let inline = cds.ql`select from EmployeeNoUnmanaged { + let inline = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{ *, furniture as building, address.city as floor, building.id as room } }` - let absolutePaths = cds.ql`select from EmployeeNoUnmanaged { + let absolutePaths = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.address.city as office_floor, office.building.id as office_room, office.furniture as office_building, @@ -310,10 +310,10 @@ describe('inline', () => { it('smart wildcard - structure overwritten by assoc before *', () => { // intermediate structures are overwritten - let inline = cds.ql`select from EmployeeNoUnmanaged { + let inline = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{ building as furniture, * } }` - let absolutePaths = cds.ql`select from EmployeeNoUnmanaged { + let absolutePaths = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.building as office_furniture, office.floor, office.room, @@ -334,10 +334,10 @@ describe('inline', () => { }) it('smart wildcard - structure overwritten by join relevant assoc before *', () => { // intermediate structures are overwritten - let inline = cds.ql`select from EmployeeNoUnmanaged { + let inline = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{ building[name='mega tower'].name as furniture, * } }` - let absolutePaths = cds.ql`select from EmployeeNoUnmanaged { + let absolutePaths = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.building[name='mega tower'].name as office_furniture, office.floor, office.room, @@ -361,10 +361,10 @@ describe('inline', () => { }) it('wildcard - no overwrite but additional cols', () => { // intermediate structures are overwritten - let inline = cds.ql`select from EmployeeNoUnmanaged { + let inline = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{ *, 'foo' as last } }` - let absolutePaths = cds.ql`select from EmployeeNoUnmanaged { + let absolutePaths = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.floor, office.room, office.building, @@ -390,7 +390,7 @@ describe('inline', () => { }) it('assigning alias within inline only influences name of element, prefix still appended', () => { // intermediate structures are overwritten - let inline = cds.ql`select from EmployeeNoUnmanaged { + let inline = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{ floor as x } }` let expected = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { @@ -401,10 +401,10 @@ describe('inline', () => { }) it('smart wildcard - structured overwrite before *', () => { // intermediate structures are overwritten - let inline = cds.ql`select from EmployeeNoUnmanaged { + let inline = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{ 'first' as furniture, 'second' as building, * } }` - let absolutePaths = cds.ql`select from EmployeeNoUnmanaged { + let absolutePaths = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { 'first' as office_furniture, 'second' as office_building, office.floor, @@ -425,10 +425,10 @@ describe('inline', () => { }) it('smart wildcard - structured overwrite after *', () => { // intermediate structures are overwritten - let inline = cds.ql`select from EmployeeNoUnmanaged { + let inline = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{*, 'third' as building, 'fourth' as address } }` - let absolutePaths = cds.ql`select from EmployeeNoUnmanaged { + let absolutePaths = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.floor, office.room, 'third' as office_building, @@ -449,10 +449,10 @@ describe('inline', () => { it('wildcard expansion - exclude association', () => { // intermediate structures are overwritten - let inline = cds.ql`select from EmployeeNoUnmanaged { + let inline = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.{*} excluding { building, address } }` - let absolutePaths = cds.ql`select from EmployeeNoUnmanaged { + let absolutePaths = cds.ql`select from EmployeeNoUnmanaged as EmployeeNoUnmanaged { office.floor, office.room, office.furniture diff --git a/db-service/test/cqn4sql/keyless.test.js b/db-service/test/cqn4sql/keyless.test.js index 06df2a30f..3b8d2976e 100644 --- a/db-service/test/cqn4sql/keyless.test.js +++ b/db-service/test/cqn4sql/keyless.test.js @@ -22,9 +22,9 @@ describe('keyless entities', () => { // ok if explicit foreign key is used const qOk = SELECT.columns('ID').from(Books).where(`authorWithExplicitForeignKey[ID = 42].name LIKE 'King'`) expect(cqn4sql(qOk, model)).to.eql( - cds.ql`SELECT Books.ID FROM Books as Books + cds.ql`SELECT $B.ID FROM Books as $B left join Authors as authorWithExplicitForeignKey - on authorWithExplicitForeignKey.ID = Books.authorWithExplicitForeignKey_ID + on authorWithExplicitForeignKey.ID = $B.authorWithExplicitForeignKey_ID and authorWithExplicitForeignKey.ID = 42 where authorWithExplicitForeignKey.name LIKE 'King'`, ) @@ -43,9 +43,9 @@ describe('keyless entities', () => { // ok if explicit foreign key is used const qOk = SELECT.from('Books:authorWithExplicitForeignKey').columns('ID') expect(cqn4sql(qOk, model)).to.eql( - cds.ql`SELECT authorWithExplicitForeignKey.ID FROM Authors as authorWithExplicitForeignKey + cds.ql`SELECT $a.ID FROM Authors as $a where exists ( - SELECT 1 from Books as Books where Books.authorWithExplicitForeignKey_ID = authorWithExplicitForeignKey.ID + SELECT 1 from Books as $B where $B.authorWithExplicitForeignKey_ID = $a.ID )`, ) }) @@ -55,9 +55,9 @@ describe('keyless entities', () => { // ok if explicit foreign key is used const qOk = SELECT.from('Books').columns('ID').where('exists authorWithExplicitForeignKey') expect(cqn4sql(qOk, model)).to.eql( - cds.ql`SELECT Books.ID FROM Books as Books + cds.ql`SELECT $B.ID FROM Books as $B where exists ( - SELECT 1 from Authors as authorWithExplicitForeignKey where authorWithExplicitForeignKey.ID = Books.authorWithExplicitForeignKey_ID + SELECT 1 from Authors as $a where $a.ID = $B.authorWithExplicitForeignKey_ID )`, ) }) @@ -70,10 +70,10 @@ describe('keyless entities', () => { cds.ql` SELECT ( - SELECT authorWithExplicitForeignKey.name from Authors as authorWithExplicitForeignKey - where Books.authorWithExplicitForeignKey_ID = authorWithExplicitForeignKey.ID + SELECT $a.name from Authors as $a + where $B.authorWithExplicitForeignKey_ID = $a.ID ) as authorWithExplicitForeignKey - from Books as Books`, + from Books as $B`, ) }) @@ -103,7 +103,7 @@ describe('keyless entities', () => { ) }) it('backlink has no foreign keys for expand subquery', () => { - const q = cds.ql`SELECT bookWithBackLink { title } from Authors` + const q = cds.ql`SELECT bookWithBackLink { title } from Authors as Authors` expect(() => cqn4sql(q, model)).to.throw( `Path step “bookWithBackLink” is a self comparison with “author” that has no foreign keys`, ) diff --git a/db-service/test/cqn4sql/localized.test.js b/db-service/test/cqn4sql/localized.test.js index 8f9467c02..4f15932fe 100644 --- a/db-service/test/cqn4sql/localized.test.js +++ b/db-service/test/cqn4sql/localized.test.js @@ -13,7 +13,7 @@ describe('localized', () => { model = await cds.load(__dirname + '/../bookshop/db/schema').then( m => cds.compile.for.nodejs(m, options)) }) it('performs no replacement if not requested', () => { - const q = cds.ql`SELECT from bookshop.Books {ID, title}` + const q = cds.ql`SELECT from bookshop.Books as Books {ID, title}` let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(cds.ql` SELECT from bookshop.Books as Books @@ -23,7 +23,7 @@ describe('localized', () => { }`) }) it('performs simple replacement of ref', () => { - const q = SELECT.localized `from bookshop.Books {ID, title}` + const q = SELECT.localized `from bookshop.Books as Books {ID, title}` let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(cds.ql` SELECT from localized.bookshop.Books as Books @@ -36,26 +36,26 @@ describe('localized', () => { const q = SELECT.localized `from bookshop.Authors {ID} where exists books[title = 'Sturmhöhe']` let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(CQL(` - SELECT from ${transitive_ ? 'localized.' : ''}bookshop.Authors as Authors + SELECT from ${transitive_ ? 'localized.' : ''}bookshop.Authors as $A { - Authors.ID, + $A.ID, } where exists ( - SELECT 1 from localized.bookshop.Books as books where books.author_ID = Authors.ID and books.title = 'Sturmhöhe' + SELECT 1 from localized.bookshop.Books as $b where $b.author_ID = $A.ID and $b.title = 'Sturmhöhe' )`)) }) it('uses localized table in where exists subquery (2)', () => { const q = SELECT.localized `from bookshop.Authors:books[title = 'Sturmhöhe'] {ID}` let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(CQL(` - SELECT from localized.bookshop.Books as books + SELECT from localized.bookshop.Books as $b { - books.ID, + $b.ID, } where exists ( - SELECT 1 from ${transitive_ ? 'localized.' : ''}bookshop.Authors as Authors where Authors.ID = books.author_ID - ) and books.title = 'Sturmhöhe'`)) + SELECT 1 from ${transitive_ ? 'localized.' : ''}bookshop.Authors as $A where $A.ID = $b.author_ID + ) and $b.title = 'Sturmhöhe'`)) }) it('performs no replacement of ref if ”@cds.localized: false”', () => { - const q = SELECT.localized `from bookshop.BP {ID, title}` + const q = SELECT.localized `from bookshop.BP as BP {ID, title}` let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(cds.ql` SELECT from bookshop.BP as BP @@ -68,13 +68,13 @@ describe('localized', () => { const q = SELECT.localized `from bookshop.BP {ID, title, currency { code } }` let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(cds.ql` - SELECT from bookshop.BP as BP + SELECT from bookshop.BP as $B { - BP.ID, - BP.title, + $B.ID, + $B.title, ( - SELECT currency.code from sap.common.Currencies as currency - where BP.currency_code = currency.code + SELECT $c.code from sap.common.Currencies as $c + where $B.currency_code = $c.code ) as currency }`) }) @@ -91,18 +91,18 @@ describe('localized', () => { } }` let expected = CQL(` - SELECT from ${transitive_?'localized.':''}bookshop.DataRestrictions as DataRestrictions { - DataRestrictions.ID, + SELECT from ${transitive_?'localized.':''}bookshop.DataRestrictions as $D { + $D.ID, ( - SELECT from ${transitive_?'localized.':''}bookshop.DataRestrictionAccessGroups as dataRestrictionAccessGroups { - dataRestrictionAccessGroups.dataRestrictionID, - dataRestrictionAccessGroups.accessGroupID, + SELECT from ${transitive_?'localized.':''}bookshop.DataRestrictionAccessGroups as $d2 { + $d2.dataRestrictionID, + $d2.accessGroupID, ( - SELECT from localized.bookshop.AccessGroups as accessGroup { - accessGroup.ID - } where accessGroup.ID = dataRestrictionAccessGroups.accessGroupID + SELECT from localized.bookshop.AccessGroups as $a { + $a.ID + } where $a.ID = $d2.accessGroupID ) as accessGroup - } where DataRestrictions.ID = dataRestrictionAccessGroups.dataRestrictionID + } where $D.ID = $d2.dataRestrictionID ) as dataRestrictionAccessGroups } `) @@ -114,25 +114,25 @@ describe('localized', () => { const q = SELECT.localized `from bookshop.BPLocalized {ID, title}` let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(cds.ql` - SELECT from localized.bookshop.BPLocalized as BPLocalized + SELECT from localized.bookshop.BPLocalized as $B { - BPLocalized.ID, - BPLocalized.title, + $B.ID, + $B.title, }`) }) it('performs simple replacement of ref within subquery', () => { const q = SELECT.localized `from bookshop.Books {ID, title, (SELECT title from bookshop.Books) as foo}` let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(cds.ql` - SELECT from localized.bookshop.Books as Books + SELECT from localized.bookshop.Books as $B { - Books.ID, - Books.title, - (SELECT Books2.title from localized.bookshop.Books as Books2) as foo + $B.ID, + $B.title, + (SELECT $B2.title from localized.bookshop.Books as $B2) as foo }`) }) it('performs simple replacement of ref within subquery in from', () => { - const q = SELECT.localized `from (SELECT Books.title from bookshop.Books) as foo { foo.title }` + const q = SELECT.localized `from (SELECT Books.title from bookshop.Books as Books) as foo { foo.title }` let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(cds.ql` SELECT from ( @@ -144,7 +144,7 @@ describe('localized', () => { ) }) it('performs no replacement of ref within subquery if main query has ”@cds.localized: false”', () => { - const q = SELECT.localized `from bookshop.BP {ID, title, (SELECT title from bookshop.Books) as foo}` + const q = SELECT.localized `from bookshop.BP as BP {ID, title, (SELECT title from bookshop.Books as Books) as foo}` let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(cds.ql` SELECT from bookshop.BP as BP @@ -156,7 +156,7 @@ describe('localized', () => { }) it('replaces ref in from with localized within join', () => { - const q = SELECT.localized `from bookshop.Books {ID, title, author.name as author}` + const q = SELECT.localized `from bookshop.Books as Books {ID, title, author.name as author}` // request localized target replacement let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(CQL(` @@ -175,12 +175,12 @@ describe('localized', () => { // request localized target replacement let query = cqn4sql(q, model) expect(cds.clone(query)).to.deep.equal(CQL(` - SELECT from localized.bookshop.Books as Books - left outer join ${transitive_ ? 'localized.' : ''}bookshop.Authors as author on author.ID = Books.author_ID - left outer join localized.bookshop.Books as books2 on books2.author_ID = author.ID + SELECT from localized.bookshop.Books as $B + left outer join ${transitive_ ? 'localized.' : ''}bookshop.Authors as author on author.ID = $B.author_ID + left outer join localized.bookshop.Books as books on books.author_ID = author.ID { - Books.ID, - books2.title as author_books_title, + $B.ID, + books.title as author_books_title, author.name as author }`)) }) @@ -189,8 +189,8 @@ describe('localized', () => { const q = SELECT.localized `from bookshop.Books { author as books { name } }` - const qx = CQL(`SELECT from localized.bookshop.Books as Books { - (SELECT books2.name from ${transitive_ ? 'localized.' : ''}bookshop.Authors as books2 where Books.author_ID = books2.ID) as books + const qx = CQL(`SELECT from localized.bookshop.Books as $B { + (SELECT $b2.name from ${transitive_ ? 'localized.' : ''}bookshop.Authors as $b2 where $B.author_ID = $b2.ID) as books }`) const res = cqn4sql(q, model) expect(cds.clone(res)).to.deep.equal(qx) @@ -200,8 +200,8 @@ describe('localized', () => { const q = SELECT.localized`from bookshop.Books { author as books { name, (SELECT title from bookshop.Books) as foo } }` - const qx = CQL(`SELECT from localized.bookshop.Books as Books { - (SELECT books2.name, (SELECT Books3.title from localized.bookshop.Books as Books3) as foo from ${transitive_ ? 'localized.' : ''}bookshop.Authors as books2 where Books.author_ID = books2.ID) as books + const qx = CQL(`SELECT from localized.bookshop.Books as $B { + (SELECT $b2.name, (SELECT $B3.title from localized.bookshop.Books as $B3) as foo from ${transitive_ ? 'localized.' : ''}bookshop.Authors as $b2 where $B.author_ID = $b2.ID) as books }`) const res = cqn4sql(q, model) expect(cds.clone(res)).to.deep.equal(qx) @@ -211,10 +211,10 @@ describe('localized', () => { const q = SELECT.localized`from bookshop.AuthorsUnmanagedBooks:books { ID }` - const qx = CQL(`SELECT from localized.bookshop.Books as books { - books.ID + const qx = CQL(`SELECT from localized.bookshop.Books as $b { + $b.ID } where exists ( - SELECT 1 from ${transitive_ ? 'localized.' : ''}bookshop.AuthorsUnmanagedBooks as AuthorsUnmanagedBooks where books.coAuthor_ID_unmanaged = AuthorsUnmanagedBooks.ID + SELECT 1 from ${transitive_ ? 'localized.' : ''}bookshop.AuthorsUnmanagedBooks as $A where $b.coAuthor_ID_unmanaged = $A.ID )`) const res = cqn4sql(q, model) expect(cds.clone(res)).to.deep.equal(qx) @@ -243,20 +243,20 @@ describe('localized', () => { ` let q2 = cqn4sql(q, stakeholderModel) expect(cds.clone(q2)).to.deep.equal(CQL(` - SELECT from localized.Boo as boos { boos.ID } WHERE EXISTS ( - SELECT 1 from localized.Foo as Foo3 WHERE Foo3.ID = boos.foo_ID + SELECT from localized.Boo as $b { $b.ID } WHERE EXISTS ( + SELECT 1 from localized.Foo as $F3 WHERE $F3.ID = $b.foo_ID ) AND ( EXISTS ( - SELECT 1 from localized.Foo as foo WHERE foo.ID = boos.foo_ID AND EXISTS ( - SELECT 1 from ${transitive_?'localized.':''}SpecialOwner2 as specialOwners - WHERE specialOwners.foo_ID = foo.ID and specialOwners.owner2_userID = $user.id + SELECT 1 from localized.Foo as $f WHERE $f.ID = $b.foo_ID AND EXISTS ( + SELECT 1 from ${transitive_?'localized.':''}SpecialOwner2 as $s + WHERE $s.foo_ID = $f.ID and $s.owner2_userID = $user.id ) ) OR EXISTS ( - SELECT 1 from localized.Foo as foo2 WHERE foo2.ID = boos.foo_ID AND EXISTS ( - SELECT 1 from ${transitive_?'localized.':''}ActiveOwner as activeOwners - WHERE activeOwners.foo_ID = foo2.ID and activeOwners.owner_userID = $user.id + SELECT 1 from localized.Foo as $f2 WHERE $f2.ID = $b.foo_ID AND EXISTS ( + SELECT 1 from ${transitive_?'localized.':''}ActiveOwner as $a + WHERE $a.foo_ID = $f2.ID and $a.owner_userID = $user.id ) ) ) diff --git a/db-service/test/cqn4sql/not-persisted.test.js b/db-service/test/cqn4sql/not-persisted.test.js index 8aa16d482..b2aa544b8 100644 --- a/db-service/test/cqn4sql/not-persisted.test.js +++ b/db-service/test/cqn4sql/not-persisted.test.js @@ -23,7 +23,7 @@ describe('not persisted', () => { describe('virtual fields', () => { it('remove from columns', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Foo { ID, virtualField }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Foo as Foo { ID, virtualField }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Foo as Foo { Foo.ID }`) }) @@ -35,7 +35,7 @@ describe('not persisted', () => { }) it('remove from columns in struc', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Foo { ID, stru }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Foo as Foo { ID, stru }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Foo as Foo { Foo.ID, Foo.stru_u, @@ -45,7 +45,7 @@ describe('not persisted', () => { it('remove from columns with path into struc', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Foo { + cds.ql`SELECT from bookshop.Foo as Foo { ID, stru.u, stru.v, @@ -62,7 +62,7 @@ describe('not persisted', () => { }) it('remove from columns via wildcard', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Foo`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Foo as Foo`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Foo as Foo { Foo.ID, Foo.toFoo_ID, @@ -72,13 +72,13 @@ describe('not persisted', () => { }) it('remove from GROUP BY', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Foo { ID } group by ID, virtualField`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Foo as Foo { ID } group by ID, virtualField`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Foo as Foo { Foo.ID } group by Foo.ID`) }) it('remove from ORDER BY', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Foo { ID, virtualField as x } + cds.ql`SELECT from bookshop.Foo as Foo { ID, virtualField as x } order by ID, x, Foo.virtualField`, model, ) @@ -89,7 +89,7 @@ describe('not persisted', () => { it('Navigation to virtual field does not cause join', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Foo { + cds.ql`SELECT from bookshop.Foo as Foo { ID, toFoo.virtualField, }`, @@ -119,12 +119,12 @@ describe('not persisted', () => { }) it('reject virtual elements in simple conditions', () => { - let query = cds.ql`SELECT from bookshop.Foo { ID } where ID = 5 and virtualField = 6` + let query = cds.ql`SELECT from bookshop.Foo as Foo { ID } where ID = 5 and virtualField = 6` expect(() => cqn4sql(query, model)).to.throw('Virtual elements are not allowed in expressions') }) it('reject virtual elements in order by', () => { - let query = cds.ql`SELECT from bookshop.Foo { ID, virtualField as x } + let query = cds.ql`SELECT from bookshop.Foo as Foo { ID, virtualField as x } order by ID, x, (Foo.toFoo.virtualField * 42)` expect(() => cqn4sql(query, model)).to.throw('Virtual elements are not allowed in expressions') }) @@ -132,7 +132,7 @@ describe('not persisted', () => { describe('paths with @cds.persistence.skip', () => { it('ignores column if assoc in path expression has target ”@cds.persistence.skip”', () => { - const q = cds.ql`SELECT from bookshop.NotSkipped { + const q = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped { ID, skipped.notSkipped.text }` const qx = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped @@ -143,7 +143,7 @@ describe('not persisted', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('ignores column if assoc in path expression has target ”@cds.persistence.skip” in order by / group by', () => { - const q = cds.ql`SELECT from bookshop.NotSkipped { + const q = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped { ID } group by skipped.notSkipped.text order by skipped.notSkipped.text` @@ -163,7 +163,7 @@ describe('not persisted', () => { // same as for virtual it('does not touch expression but renders the potentially wrong SQL', () => { - const q = cds.ql`SELECT from bookshop.NotSkipped { + const q = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped { ID, skipped.notSkipped.text * 2 + 5 as bar } where (skipped.notSkipped.text / 2 + 5) = 42` const qx = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped @@ -177,7 +177,7 @@ describe('not persisted', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('No join for a skip path within filter if outer path is not persisted', () => { - const q = cds.ql`SELECT from bookshop.NotSkipped { + const q = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped { ID, skipped[notSkipped.ID = 42].notSkipped.text }` const qx = cds.ql`SELECT from bookshop.NotSkipped as NotSkipped @@ -189,7 +189,7 @@ describe('not persisted', () => { }) it('Join for a skip path within filter if outer path is persisted', () => { - const q = cds.ql`SELECT from bookshop.SkippedAndNotSkipped { + const q = cds.ql`SELECT from bookshop.SkippedAndNotSkipped as SkippedAndNotSkipped { ID, self[skipped.ID = 42].ID }` const qx = cds.ql`SELECT from bookshop.SkippedAndNotSkipped as SkippedAndNotSkipped @@ -202,7 +202,7 @@ describe('not persisted', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) }) it('Join for a skip path within filter if outer path is persisted in order by', () => { - const q = cds.ql`SELECT from bookshop.SkippedAndNotSkipped { + const q = cds.ql`SELECT from bookshop.SkippedAndNotSkipped as SkippedAndNotSkipped { ID } order by self[skipped.ID = 42].ID` const qx = cds.ql`SELECT from bookshop.SkippedAndNotSkipped as SkippedAndNotSkipped @@ -215,7 +215,7 @@ describe('not persisted', () => { }) it('do not remove from simple conditions', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.NotSkipped { ID } where skipped.notSkipped.text`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.NotSkipped as NotSkipped { ID } where skipped.notSkipped.text`, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.NotSkipped as NotSkipped left outer join bookshop.Skip as skipped on skipped.ID = NotSkipped.skipped_ID diff --git a/db-service/test/cqn4sql/path-in-from.test.js b/db-service/test/cqn4sql/path-in-from.test.js index 6a18858b9..6d475e9b2 100644 --- a/db-service/test/cqn4sql/path-in-from.test.js +++ b/db-service/test/cqn4sql/path-in-from.test.js @@ -16,12 +16,12 @@ describe('infix filter on entities', () => { }) it('handles simple infix filter at entity', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books[price < 12.13] {ID}`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books[price < 12.13] as Books {ID}`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books {Books.ID} WHERE Books.price < 12.13`) }) it('handles multiple simple infix filters at entity', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books[price < 12.13 or 12.14 < price] {ID}`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books[price < 12.13 or 12.14 < price] as Books {ID}`, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Books as Books {Books.ID} WHERE (Books.price < 12.13 or 12.14 < Books.price)`, ) @@ -34,13 +34,13 @@ describe('infix filter on entities', () => { }) it('handles infix filter with struct access at entity', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books[dedication.text = 'foo'] {Books.ID}`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books[dedication.text = 'foo'] as Books {Books.ID}`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books {Books.ID} WHERE Books.dedication_text = 'foo'`) }) // TODO belongs to flattening it('handles infix filter at entity with association if it accesses FK', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books[author.ID = 22] {Books.ID}`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books[author.ID = 22] as Books {Books.ID}`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books {Books.ID} WHERE Books.author_ID = 22`) }) it('handles query modifiers defined in infix filter at leaf', () => { @@ -51,7 +51,7 @@ describe('infix filter on entities', () => { having title order by title desc limit 2 - ] {ID}`, model) + ] as Books {ID}`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books {Books.ID} WHERE Books.price < 12.13 GROUP BY Books.title HAVING Books.title ORDER BY Books.title DESC LIMIT 2`) }); it('merges query modifiers defined in infix filter at leaf with those defined at query root', () => { @@ -62,7 +62,7 @@ describe('infix filter on entities', () => { having title order by title desc limit 2 - ] {ID} where price > 5 group by price having price order by price limit 5`, model) + ] as Books {ID} where price > 5 group by price having price order by price limit 5`, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Books as Books {Books.ID} WHERE (Books.price > 5) and (Books.price < 12.13) @@ -75,9 +75,9 @@ describe('infix filter on entities', () => { it('handles query modifiers (where only) along the ref of a scoped query', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books[where title = 'bar']:author[group by name] {ID}`, model) const expected = cds.ql` - SELECT from bookshop.Authors as author {author.ID} where exists ( - SELECT 1 from bookshop.Books as Books WHERE Books.author_ID = author.ID and Books.title = 'bar' - ) GROUP BY author.name + SELECT from bookshop.Authors as $a {$a.ID} where exists ( + SELECT 1 from bookshop.Books as $B WHERE $B.author_ID = $a.ID and $B.title = 'bar' + ) GROUP BY $a.name ` expect(query).to.deep.equal(expected) }); diff --git a/db-service/test/cqn4sql/pseudo-variable-replacement.test.js b/db-service/test/cqn4sql/pseudo-variable-replacement.test.js index f91f5cd73..3665c23db 100644 --- a/db-service/test/cqn4sql/pseudo-variable-replacement.test.js +++ b/db-service/test/cqn4sql/pseudo-variable-replacement.test.js @@ -20,7 +20,7 @@ describe('Pseudo Variables', () => { it('stay untouched in SELECT', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, $user, $user.id, @@ -57,7 +57,7 @@ describe('Pseudo Variables', () => { it('stay untouched in WHERE/GROUP BY/ORDER BY', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID } WHERE $user = 'karl' and $user.locale = 'DE' and $user.unknown.foo.bar = 'foo' GROUP BY $user.id, $to @@ -76,7 +76,7 @@ describe('Pseudo Variables', () => { it('stay untouched in filter', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, author[name = $user.name or dateOfDeath < $now].dateOfBirth }`, @@ -93,7 +93,7 @@ describe('Pseudo Variables', () => { it('stay untouched in generated join', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.SimpleBook { + cds.ql`SELECT from bookshop.SimpleBook as SimpleBook { ID } where activeAuthors.name = $user.name`, model, @@ -115,16 +115,17 @@ describe('Pseudo Variables', () => { model, ) - const expected = cds.ql`SELECT from bookshop.Books as Books - { Books.ID } where exists ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID and $user.name = 'towald' - ) + const expected = cds.ql`SELECT from bookshop.Books as $B + { $B.ID } + where exists ( + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID and $user.name = 'towald' + ) ` expect(query).to.deep.equal(expected) }) it('must not be prefixed by table alias', () => { - expect(() => cqn4sql(cds.ql`SELECT from bookshop.Books { ID, Books.$now }`, model)).to.throw( + expect(() => cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, Books.$now }`, model)).to.throw( '"$now" not found in "bookshop.Books"', ) }) diff --git a/db-service/test/cqn4sql/replacements.test.js b/db-service/test/cqn4sql/replacements.test.js index 50fcb0e99..e463be4ad 100644 --- a/db-service/test/cqn4sql/replacements.test.js +++ b/db-service/test/cqn4sql/replacements.test.js @@ -14,6 +14,7 @@ describe('in where', () => { it('replace ` in ` in where', () => { const original = SELECT.from('bookshop.Books') + .alias('Books') .columns(['ID']) .where({ ID: { in: [] } }) @@ -24,7 +25,7 @@ describe('in where', () => { ) }) it('replace `not in ` in where', () => { - const original = SELECT.from('bookshop.Books').columns(['ID']) + const original = SELECT.from('bookshop.Books').alias('Books').columns(['ID']) original.SELECT.where = [{ ref: ['ID'] }, 'not', 'in', { list: [] }] expect(cqn4sql(original, model)).to.deep.equal( @@ -34,7 +35,9 @@ describe('in where', () => { ) }) it('replace `in ` in join condition induced by infix filter', () => { - const query = SELECT.from('bookshop.Books').columns({ + const query = SELECT.from('bookshop.Books') + .alias('Books') + .columns({ ref: [{ id: 'author', where: [{ ref: ['name'] }, 'not', 'in', { list: [] }] }, 'ID'], }) @@ -56,10 +59,10 @@ describe('in where', () => { expect(cqn4sql(query, model)).to.deep.equal( cds.ql` - SELECT from bookshop.Authors as author - { author.ID } + SELECT from bookshop.Authors as $a + { $a.ID } where exists ( - SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID and Books.title is not null + SELECT 1 from bookshop.Books as $B where $B.author_ID = $a.ID and $B.title is not null ) `, ) diff --git a/db-service/test/cqn4sql/search.test.js b/db-service/test/cqn4sql/search.test.js index 8e39e9614..a5fe4e3f4 100644 --- a/db-service/test/cqn4sql/search.test.js +++ b/db-service/test/cqn4sql/search.test.js @@ -33,7 +33,7 @@ describe('Replace attribute search by search predicate', () => { }) it('multiple string elements', () => { - let query = cds.ql`SELECT from bookshop.Genres { ID }` + let query = cds.ql`SELECT from bookshop.Genres as Genres { ID }` query.SELECT.search = [{ val: 'x' }, 'or', { val: 'y' }] let res = cqn4sql(query, model) @@ -43,7 +43,7 @@ describe('Replace attribute search by search predicate', () => { }) it('with existing WHERE clause', () => { - let query = cds.ql`SELECT from bookshop.Genres { ID } where ID < 4 or ID > 5` + let query = cds.ql`SELECT from bookshop.Genres as Genres { ID } where ID < 4 or ID > 5` query.SELECT.search = [{ val: 'x' }, 'or', { val: 'y' }] let res = cqn4sql(query, model) @@ -54,7 +54,7 @@ describe('Replace attribute search by search predicate', () => { }) it('with filter on data source', () => { - let query = cds.ql`SELECT from bookshop.Genres[ID < 4 or ID > 5] { ID }` + let query = cds.ql`SELECT from bookshop.Genres[ID < 4 or ID > 5] as Genres { ID }` query.SELECT.search = [{ val: 'x' }, 'or', { val: 'y' }] let res = cqn4sql(query, model) @@ -67,7 +67,7 @@ describe('Replace attribute search by search predicate', () => { }) it('string fields inside struct', () => { - let query = cds.ql`SELECT from bookshop.Person { ID }` + let query = cds.ql`SELECT from bookshop.Person as Person { ID }` query.SELECT.search = [{ val: 'x' }, 'or', { val: 'y' }] let res = cqn4sql(query, model) @@ -77,7 +77,7 @@ describe('Replace attribute search by search predicate', () => { }) it('ignores virtual string elements', () => { - let query = cds.ql`SELECT from bookshop.Foo { ID }` + let query = cds.ql`SELECT from bookshop.Foo as Foo { ID }` query.SELECT.search = [{ val: 'x' }, 'or', { val: 'y' }] let res = cqn4sql(query, model) @@ -86,7 +86,7 @@ describe('Replace attribute search by search predicate', () => { }`) }) it('Uses primary query source in case of joins', () => { - let query = cds.ql`SELECT from bookshop.Books { ID, author.books.title as authorsBook }` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, author.books.title as authorsBook }` query.SELECT.search = [{ val: 'x' }, 'or', { val: 'y' }] let res = cqn4sql(query, model) @@ -103,7 +103,7 @@ describe('Replace attribute search by search predicate', () => { }) it('Search columns if result is grouped', () => { // in this case, we actually search the "title" which comes from the join - let query = cds.ql`SELECT from bookshop.Books { ID, author.books.title as authorsBook } group by title` + let query = cds.ql`SELECT from bookshop.Books as Books { ID, author.books.title as authorsBook } group by title` query.SELECT.search = [{ val: 'x' }, 'or', { val: 'y' }] let res = cqn4sql(query, model) @@ -119,7 +119,7 @@ describe('Replace attribute search by search predicate', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(expected) }) it('Search on navigation', () => { - let query = cds.ql`SELECT from bookshop.Authors:books { ID }` + let query = cds.ql`SELECT from bookshop.Authors:books as books { ID }` query.SELECT.search = [{ val: 'x' }, 'or', { val: 'y' }] let res = cqn4sql(query, model) @@ -129,8 +129,8 @@ describe('Replace attribute search by search predicate', () => { books.ID, } where exists ( - SELECT 1 from bookshop.Authors as Authors - where Authors.ID = books.author_ID + SELECT 1 from bookshop.Authors as $A + where $A.ID = books.author_ID ) and search((books.createdBy, books.modifiedBy, books.anotherText, books.title, books.descr, books.currency_code, books.dedication_text, books.dedication_sub_foo, books.dedication_dedication), ('x' OR 'y')) ` @@ -142,6 +142,7 @@ describe('Replace attribute search by search predicate', () => { // if we search on aggregated results, the search must be put into the having clause const { Books } = cds.entities let query = SELECT.from(Books) + .alias('Books') .columns({ args: [{ ref: ['title'] }], as: 'firstInAlphabet', func: 'MIN' }) .groupBy('title') .search('Cat') @@ -155,7 +156,7 @@ describe('Replace attribute search by search predicate', () => { it('Ignore non string aggregates from being searched', () => { const query = cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { title, AVG(Books.stock) as searchRelevant, } group by title @@ -172,7 +173,7 @@ describe('Replace attribute search by search predicate', () => { }) it('aggregations which are not of type string are not searched', () => { const query = cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { ID, SUM(Books.stock) as notSearchRelevant, } group by title @@ -190,7 +191,7 @@ describe('Replace attribute search by search predicate', () => { // this aggregation is not relevant for search per default // but due to the cast to string, we search const query = cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { ID, substring(Books.stock) as searchRelevantViaCast: cds.String, } group by title @@ -212,7 +213,7 @@ describe('Replace attribute search by search predicate', () => { // this aggregation is not relevant for search per default // but due to the cast to string, we search const query = cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { ID, ('very' + 'useful' + 'string') as searchRelevantViaCast: cds.String, ('1' + '2' + '3') as notSearchRelevant: cds.Integer, @@ -245,7 +246,7 @@ describe('search w/ path expressions', () => { }) it('one string element with one search element', () => { - let query = cds.ql`SELECT from search.BooksSearchAuthorName { ID, title }` + let query = cds.ql`SELECT from search.BooksSearchAuthorName as BooksSearchAuthorName { ID, title }` query.SELECT.search = [{ val: 'x' }] let res = cqn4sql(query, model) @@ -290,7 +291,7 @@ describe('search w/ path expressions', () => { }) it('dont dump for non existing search paths, but ignore the path', () => { - let query = cds.ql`SELECT from search.BookShelf { ID, genre }` + let query = cds.ql`SELECT from search.BookShelf as BookShelf { ID, genre }` query.SELECT.search = [{ val: 'Harry Plotter' }] let res = cqn4sql(query, model) diff --git a/db-service/test/cqn4sql/structure-access.test.js b/db-service/test/cqn4sql/structure-access.test.js index 02a25118f..a04582813 100644 --- a/db-service/test/cqn4sql/structure-access.test.js +++ b/db-service/test/cqn4sql/structure-access.test.js @@ -16,7 +16,7 @@ describe('Structured Access', () => { // see "±" there we must address the flat name // of the column in the order by clause it('resolves struct path to flat field', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { dedication.text }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { dedication.text }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.dedication_text }`) }) @@ -32,26 +32,26 @@ describe('Structured Access', () => { ) }) it('resolves first path step as element of data source if it cannot be resolved as table alias', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { dedication.text }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { dedication.text }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.dedication_text }`) }) it('cannot access flat names of structure elements', () => { - expect(() => cqn4sql(cds.ql`SELECT from bookshop.Books { dedication_text }`, model)).to.throw( + expect(() => cqn4sql(cds.ql`SELECT from bookshop.Books as Books { dedication_text }`, model)).to.throw( /"dedication_text" not found in the elements of "bookshop.Books"/, ) }) it('cannot access flat names of FKs', () => { - expect(() => cqn4sql(cds.ql`SELECT from bookshop.Books { author_ID }`, model)).to.throw( + expect(() => cqn4sql(cds.ql`SELECT from bookshop.Books as Books { author_ID }`, model)).to.throw( /"author_ID" not found in the elements of "bookshop.Books"/, ) }) it('deeply structured access', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, dedication.sub.foo }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, dedication.sub.foo }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID, Books.dedication_sub_foo }`) }) // mess around with table alias it('using implicit table alias', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { Books.dedication.text }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { Books.dedication.text }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.dedication_text }`) }) @@ -82,7 +82,7 @@ describe('Structured Access', () => { }) it('unfolds all leafs of sub structure', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { ID, dedication.text, Books.dedication.sub, @@ -121,7 +121,7 @@ describe('Structured Access', () => { describe('in Where', () => { it('simple access of sub element', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID } + cds.ql`SELECT from bookshop.Books as Books { ID } WHERE dedication.text = 'For Mummy'`, model, ) @@ -137,7 +137,7 @@ describe('Structured Access', () => { // flat, implicit alias of column must be used it('±', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { dedication.sub } + cds.ql`SELECT from bookshop.Books as Books { dedication.sub } ORDER BY dedication_sub.foo, Books.dedication.text, dedication.text`, model, ) @@ -195,7 +195,7 @@ describe('Structured Access', () => { describe('in expressions', () => { it('access leaf of structured function argument in column', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { power(Books.dedication.text, 2*dedication.sub.foo) as path }`, model, @@ -206,7 +206,7 @@ describe('Structured Access', () => { }) it('functions in WHERE', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID } + cds.ql`SELECT from bookshop.Books as Books { ID } WHERE power(Books.dedication.text, 2*dedication.sub.foo) > dedication.dedication+2`, model, ) @@ -215,7 +215,7 @@ describe('Structured Access', () => { }) it('functions in GROUP BY/HAVING', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID } + cds.ql`SELECT from bookshop.Books as Books { ID } GROUP BY power(Books.dedication.text, 2*dedication.sub.foo), dedication.dedication+2 HAVING power(Books.dedication.text, 2*dedication.sub.foo) > dedication.dedication+2`, model, @@ -226,7 +226,7 @@ describe('Structured Access', () => { }) it('functions in ORDER BY', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID } + cds.ql`SELECT from bookshop.Books as Books { ID } ORDER BY power(dedication.text, 2*dedication.sub.foo), dedication.dedication+2`, model, ) @@ -244,7 +244,7 @@ describe('Structured Access', () => { describe('in subqueries', () => { it('subquery in from with alias', () => { let query = cqn4sql( - cds.ql`SELECT from (select from bookshop.Books { + cds.ql`SELECT from (select from bookshop.Books as Books { ID, dedication.sub.foo as foo }) as Bar { ID, foo }`, @@ -279,13 +279,13 @@ describe('Structured Access', () => { // a path along a managed association to a target field that is used as FK of the association // is not translated into a join (or subquery), but as struct access to the local FK element it('access fk in column', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, Books.currency.code }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, Books.currency.code }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID, Books.currency_code }`) }) it('structured fk', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, Books.dedication.addressee.ID as dedicationAddressee }`, + cds.ql`SELECT from bookshop.Books as Books { ID, Books.dedication.addressee.ID as dedicationAddressee }`, model, ) expect(query).to.deep.equal( @@ -323,7 +323,7 @@ describe('Structured Access', () => { it('optimizes assoc.fk path in expression', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { + cds.ql`SELECT from bookshop.Books as Books { power(author.ID, 2*dedication.addressee.ID) as path }`, model, @@ -334,7 +334,7 @@ describe('Structured Access', () => { }) it('resolves struct paths into FROM subquery mix with assoc FK access', () => { let query = cqn4sql( - cds.ql`SELECT from (select from bookshop.Books {Books.ID, Books.dedication as dedi}) as Bar { ID, dedi.addressee.ID}`, + cds.ql`SELECT from (select from bookshop.Books as Books {Books.ID, Books.dedication as dedi}) as Bar { ID, dedi.addressee.ID}`, model, ) expect(query).to.deep.equal( @@ -352,7 +352,7 @@ describe('Structured Access', () => { describe('in GROUP BY/HAVING', () => { it('uses query source elements and not column alias', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { dedication.text } + cds.ql`SELECT from bookshop.Books as Books { dedication.text } GROUP BY dedication.text HAVING dedication.text = 'For Mummy'`, model, ) @@ -362,7 +362,7 @@ describe('Structured Access', () => { it('resolves and unfolds struct paths ending on struct', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { dedication.sub } + cds.ql`SELECT from bookshop.Books as Books { dedication.sub } GROUP BY dedication.sub`, model, ) diff --git a/db-service/test/cqn4sql/table-alias.test.js b/db-service/test/cqn4sql/table-alias.test.js index dcadbd49c..779b6ff52 100644 --- a/db-service/test/cqn4sql/table-alias.test.js +++ b/db-service/test/cqn4sql/table-alias.test.js @@ -9,19 +9,99 @@ describe('table alias access', () => { model = cds.model = await cds.load(__dirname + '/../bookshop/db/schema').then(cds.linked) }) + describe('implicit aliasing', () => { + it('can handle entities beginning with $', () => { + const query = cds.ql`SELECT from bookshop.![$special] { ID }` + const result = cqn4sql(query, model) + expect(result).to.deep.equal(cds.ql`SELECT from bookshop.$special as $s { $s.ID }`) + }) + // TODO: also use technical alias for join nodes + it('can handle entities beginning with $ and joins for assocs starting with $', () => { + const query = cds.ql`SELECT from bookshop.![$special] { ID, ![$special].name }` + const result = cqn4sql(query, model) + expect(result).to.deep.equal( + cds.ql`SELECT from bookshop.$special as $s left join bookshop.$special as $special on $special.ID = $s.$special_ID + { + $s.ID, + $special.name as $special_name + }` + ) + }) + it('can handle scoped queries via navigations starting with $', () => { + const query = cds.ql`SELECT from bookshop.$special:$special { ID }` + const result = cqn4sql(query, model) + expect(result).to.deep.equal( + cds.ql` + SELECT from bookshop.$special as $s { $s.ID } + where exists (SELECT 1 from bookshop.$special as $s2 where $s2.$special_ID = $s.ID) + `) + }) + it('can handle expand queries via navigations starting with $', () => { + const query = cds.ql`SELECT from bookshop.$special { ID, $special { name } }` + const result = cqn4sql(query, model) + expect(JSON.parse(JSON.stringify(result))).to.deep.equal( + cds.ql` + SELECT from bookshop.$special as $s { + $s.ID, + (SELECT $s2.name from bookshop.$special as $s2 where $s.$special_ID = $s2.ID) as $special + } + `) + }) + + // entity called "$" with association called "$" to entity called "$" + it('can handle entities beginning with $', () => { + const query = cds.ql`SELECT from bookshop.$ { ID }` + const result = cqn4sql(query, model) + expect(result).to.deep.equal(cds.ql`SELECT from bookshop.$ as $$ { $$.ID }`) + }) + + // TODO: also use technical alias for join nodes + it('can handle entities called $ and joins for assocs called $', () => { + const query = cds.ql`SELECT from bookshop.$ { ID, $.name }` + const result = cqn4sql(query, model) + expect(result).to.deep.equal( + cds.ql`SELECT from bookshop.$ as $$ left join bookshop.$ as $ on $.ID = $$.$_ID + { + $$.ID, + $.name as $_name + }` + ) + }) + + it('can handle scoped queries via navigations called $', () => { + const query = cds.ql`SELECT from bookshop.$:$ { ID }` + const result = cqn4sql(query, model) + expect(result).to.deep.equal( + cds.ql` + SELECT from bookshop.$ as $$ { $$.ID } + where exists (SELECT 1 from bookshop.$ as $$2 where $$2.$_ID = $$.ID) + `) + }) + + it('can handle expand queries via navigations called $', () => { + const query = cds.ql`SELECT from bookshop.$ { ID, $ { name } }` + const result = cqn4sql(query, model) + expect(JSON.parse(JSON.stringify(result))).to.deep.equal( + cds.ql` + SELECT from bookshop.$ as $$ { + $$.ID, + (SELECT $$2.name from bookshop.$ as $$2 where $$.$_ID = $$2.ID) as $ + } + `) + }) + + }) + describe('in columns', () => { - // For the time being, we always add a table alias for field accesses. - // On DB, the table name is bookshop_Books rather than Books - // -> if Books is used as table alias, we need to explicitly define this alias it('makes implicit table alias explicit and uses it for access', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID }`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID }`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID }`) }) it('creates unique alias for anonymous query which selects from other query', () => { let query = cqn4sql(cds.ql`SELECT from (SELECT from bookshop.Books { ID } )`, model) expect(query).to.deep.equal( - cds.ql`SELECT from (SELECT from bookshop.Books as Books { Books.ID }) as __select__ { __select__.ID }`, + cds.ql`SELECT from (SELECT from bookshop.Books as $B { $B.ID }) as __select__ { __select__.ID }`, ) }) @@ -52,7 +132,7 @@ describe('table alias access', () => { cds.ql` SELECT from ( SELECT from ( - SELECT from bookshop.Books { ID, author } + SELECT from bookshop.Books as Books { ID, author } ) ) as __select__ { @@ -74,12 +154,12 @@ describe('table alias access', () => { }) it('preserves table alias at field access', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { Books.ID }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { Books.ID }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID }`) }) it('handles field access with and without table alias', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, Books.stock }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID, Books.stock }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID, Books.stock }`) }) @@ -90,18 +170,20 @@ describe('table alias access', () => { it('user defined table alias equals field name', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books as stock { stock.ID, stock, stock.stock as s2 }`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as stock { stock.ID, stock.stock, stock.stock as s2 }`) + expect(query).to.deep.equal( + cds.ql`SELECT from bookshop.Books as stock { stock.ID, stock.stock, stock.stock as s2 }`, + ) }) it('supports scoped entity names', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books.twin { ID }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books.twin as twin { ID }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books.twin as twin { twin.ID }`) }) }) describe('in WHERE, GROUP BY, HAVING', () => { it('WHERE with implicit table alias', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } WHERE ID = 1 and Books.stock <> 1`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } WHERE ID = 1 and Books.stock <> 1`, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Books as Books { Books.ID } WHERE Books.ID = 1 and Books.stock <> 1`, ) @@ -111,7 +193,7 @@ describe('table alias access', () => { const query = { SELECT: { columns: [{ ref: ['ID'] }, { ref: ['?'], param: true, as: 'discount' }], - from: { ref: ['bookshop.Books'] }, + from: { ref: ['bookshop.Books'], as: 'Books' }, where: [{ ref: ['ID'] }, '=', { ref: ['?'], param: true }], }, } @@ -122,7 +204,9 @@ describe('table alias access', () => { it('WHERE with explicit table alias', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Bar { ID } WHERE ID = 1 and Bar.stock <> 1`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Bar { Bar.ID } WHERE Bar.ID = 1 and Bar.stock <> 1`) + expect(query).to.deep.equal( + cds.ql`SELECT from bookshop.Books as Bar { Bar.ID } WHERE Bar.ID = 1 and Bar.stock <> 1`, + ) }) it('WHERE with explicit table alias that equals field name', () => { @@ -134,7 +218,7 @@ describe('table alias access', () => { it('allows access to and prepends table alias in GROUP BY/HAVING clause', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { stock } + cds.ql`SELECT from bookshop.Books as Books { stock } group by stock, Books.title having stock > 5 and Books.title = 'foo'`, model, ) @@ -147,15 +231,15 @@ describe('table alias access', () => { // --> make sure we cover this path and prepend aliases let query = cds.ql` SELECT ID - from bookshop.Books + from bookshop.Books as Books where not exists coAuthorUnmanaged[not (name = 'King')] order by ID asc ` let expected = cds.ql` SELECT Books.ID from bookshop.Books as Books where not exists ( - SELECT 1 from bookshop.Authors as coAuthorUnmanaged - where coAuthorUnmanaged.ID = Books.coAuthor_ID_unmanaged and not (coAuthorUnmanaged.name = 'King') + SELECT 1 from bookshop.Authors as $c + where $c.ID = Books.coAuthor_ID_unmanaged and not ($c.name = 'King') ) order by ID asc ` @@ -169,7 +253,7 @@ describe('table alias access', () => { // --> make sure we cover this path and prepend aliases let query = cds.ql` SELECT ID - from bookshop.Books + from bookshop.Books as Books having coAuthorUnmanaged[not (name = 'King')].name order by ID asc ` @@ -190,7 +274,7 @@ describe('table alias access', () => { // --> make sure we cover this path and prepend aliases let query = cds.ql` SELECT ID - from bookshop.Books + from bookshop.Books as Books group by coAuthorUnmanaged[not (name = 'King')].name order by ID asc ` @@ -210,7 +294,7 @@ describe('table alias access', () => { // --> make sure we cover this path and prepend aliases let query = cds.ql` SELECT ID - from bookshop.Books + from bookshop.Books as Books order by coAuthorUnmanaged[not (name = 'King')].name ` let expected = cds.ql` @@ -229,7 +313,7 @@ describe('table alias access', () => { it('function in filter in order by', () => { let query = cds.ql` SELECT ID - from bookshop.Books + from bookshop.Books as Books order by coAuthorUnmanaged[not (calculateName(ID) = 'King')].name ` let expected = cds.ql` @@ -249,7 +333,7 @@ describe('table alias access', () => { SELECT ID, coAuthorUnmanaged[not (calculateName(ID) = 'King')].name - from bookshop.Books + from bookshop.Books as Books ` let expected = cds.ql` SELECT @@ -269,16 +353,16 @@ describe('table alias access', () => { SELECT ID, iSimilar { name } - from bookshop.Posts` + from bookshop.Posts as Posts ` const expected = cds.ql` SELECT Posts.ID, ( - SELECT from bookshop.Posts as iSimilar { - iSimilar.name + SELECT from bookshop.Posts as $i { + $i.name } - where UPPER(Posts.name) = UPPER(iSimilar.name) + where UPPER(Posts.name) = UPPER($i.name) ) as iSimilar from bookshop.Posts as Posts` @@ -290,16 +374,16 @@ describe('table alias access', () => { SELECT ID, iSimilarNested { name } - from bookshop.Posts` + from bookshop.Posts as Posts` const expected = cds.ql` SELECT Posts.ID, ( - SELECT from bookshop.Posts as iSimilarNested { - iSimilarNested.name + SELECT from bookshop.Posts as $i { + $i.name } - where UPPER(iSimilarNested.name) = UPPER(LOWER(UPPER(Posts.name)), Posts.name) + where UPPER($i.name) = UPPER(LOWER(UPPER(Posts.name)), Posts.name) ) as iSimilarNested from bookshop.Posts as Posts` @@ -335,7 +419,7 @@ describe('table alias access', () => { `) }) it('refer to other query element', () => { - const q = cds.ql`SELECT from bookshop.Books { + const q = cds.ql`SELECT from bookshop.Books as Books { Books.title, title as title2, dedication as struct, @@ -369,7 +453,7 @@ describe('table alias access', () => { }) it('late replace join relevant paths', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors { + cds.ql`SELECT from bookshop.Authors as Authors { Authors.name as author, $self.book as dollarSelfBook, books.title as book, @@ -388,7 +472,7 @@ describe('table alias access', () => { }) it('in aggregation', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors { + cds.ql`SELECT from bookshop.Authors as Authors { name as author, 1+1 as xpr, years_between(dateOfBirth, dateOfDeath) as age @@ -420,8 +504,8 @@ describe('table alias access', () => { model, ) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Authors as Authors { - Authors.name as author, + cds.ql`SELECT from bookshop.Authors as $A { + $A.name as author, 1+1 as xpr, } having (1+1) = 2 @@ -439,8 +523,8 @@ describe('table alias access', () => { model, ) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Authors as Authors { - Authors.name as author, + cds.ql`SELECT from bookshop.Authors as $A { + $A.name as author, 1+1 as xpr, } where 2 / (1+1) = 1 @@ -449,7 +533,7 @@ describe('table alias access', () => { }) it('refer to my own column in function expression', () => { const q = cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { cast('2007-07-07' as Date) as twoLeapYearsEarlier, cast('2013-07-06' as Date) as twoLeapYearsLater, months_between($self.twoLeapYearsEarlier, $self.twoLeapYearsLater) @@ -472,7 +556,7 @@ describe('table alias access', () => { }) it('refer to my own column in calc expression', () => { const q = cds.ql` - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { (cast('2007-07-07' as Date) + 1) as twoLeapYearsEarlier, (cast('2013-07-06' as Date) + 1) as twoLeapYearsLater, $self.twoLeapYearsEarlier + months_between($self.twoLeapYearsEarlier + 15) as calc @@ -518,16 +602,17 @@ describe('table alias access', () => { // Note: elements of data source can be used in ORDER BY w/o table alias (-> price) it('prefer query elements over elements of data source', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, ID as stock, ID as x } + cds.ql`SELECT from bookshop.Books as Books { ID, ID as stock, ID as x } order by ID, stock, Books.stock, price, x`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID, Books.ID as stock, Books.ID as x } + expect(query).to.deep + .equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID, Books.ID as stock, Books.ID as x } order by ID, stock, Books.stock, Books.price, x`) }) it('prefers to resolve name in ORDER BY as select item (1)', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID as Books } ORDER BY Books`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID as Books } ORDER BY Books`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID as Books } ORDER BY Books`) }) @@ -539,7 +624,7 @@ describe('table alias access', () => { it('respects sort property also for expressions/functions', () => { const original = cds.ql`SELECT from ( - select from bookshop.Books { ID as Books } ORDER BY Books desc, sum(1+1) asc + select from bookshop.Books as Books { ID as Books } ORDER BY Books desc, sum(1+1) asc ) as sub ORDER BY Books asc, 1+1 asc` const expected = cds.ql`SELECT sub.Books from ( @@ -563,6 +648,7 @@ describe('table alias access', () => { // see also https://github.com/cap-js/cds-dbs/issues/543 const query = SELECT.localized .from('bookshop.Books') + .alias('Books') .columns('title', 'title as foo', 'author.name as author') .orderBy('title', 'foo') let res = cqn4sql(query, model) @@ -587,11 +673,11 @@ describe('table alias access', () => { const expected = cds.ql` SELECT from (SELECT - SimpleBook.ID, - SimpleBook.title, - SimpleBook.author_ID - from bookshop.SimpleBook as SimpleBook - order by SimpleBook.title + $S.ID, + $S.title, + $S.author_ID + from bookshop.SimpleBook as $S + order by $S.title ) __select__ { __select__.ID, @@ -604,7 +690,7 @@ describe('table alias access', () => { expect(JSON.parse(JSON.stringify(res))).to.deep.equal(expected) }) it('same as above but descriptors like "asc", "desc" etc. must be kept', () => { - const query = cds.ql`SELECT from bookshop.Books { + const query = cds.ql`SELECT from bookshop.Books as Books { title, title as foo, author.name as author @@ -631,8 +717,8 @@ describe('table alias access', () => { query.SELECT.localized = true let res = cqn4sql(query, model) expect(JSON.parse(JSON.stringify(res))).to.deep.equal(cds.ql` - SELECT from bookshop.Books as Books - left join bookshop.Authors as author on author.ID = Books.author_ID + SELECT from bookshop.Books as $B + left join bookshop.Authors as author on author.ID = $B.author_ID { 'simple string' as foo: cds.String, substring('simple string') as bar: cds.String, @@ -644,7 +730,7 @@ describe('table alias access', () => { it('supports ORDER BY clause with expressions', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, ID as stock, ID as x } + cds.ql`SELECT from bookshop.Books as Books { ID, ID as stock, ID as x } order by ID + stock + Books.stock + price, stock, x`, model, ) @@ -661,24 +747,30 @@ describe('table alias access', () => { it('should be possible to address alias of function', () => { let input = cds.ql`SELECT from bookshop.Books { func() as bubu } order by bubu` let query = cqn4sql(input, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { func() as bubu } order by bubu`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { func() as bubu } order by bubu`) }) it('anonymous function gets proper alias', () => { let input = cds.ql`SELECT from bookshop.Books { func() }` let query = cqn4sql(input, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { func() as func }`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { func() as func }`) }) it('anonymous function gets proper alias and can be addressed in order by', () => { let input = cds.ql`SELECT from bookshop.Books { func() } order by func` let query = cqn4sql(input, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { func() as func } order by func`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { func() as func } order by func`) }) it('do not try to resolve ref in columns if columns consists of star', () => { let input = cds.ql`SELECT from bookshop.SimpleBook { * } order by author.name` let query = cqn4sql(input, model) - const expected = cds.ql`SELECT from bookshop.SimpleBook as SimpleBook left join bookshop.Authors as author on author.ID = SimpleBook.author_ID - { SimpleBook.ID, SimpleBook.title, SimpleBook.author_ID } order by author.name` + const expected = cds.ql` + SELECT from bookshop.SimpleBook as $S + left join bookshop.Authors as author on author.ID = $S.author_ID + { + $S.ID, + $S.title, + $S.author_ID + } order by author.name` expect(query).to.deep.equal(expected) }) // doesnt work, can't join with the query source itself @@ -698,31 +790,31 @@ describe('table alias access', () => { ID, ( SELECT from bookshop.Books { - Books.ID, - } where Books.ID = 1 + $B.ID, + } where $B.ID = 1 ) as sub - } where Books.ID = 1 + } where $B.ID = 1 `, model, ) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books { - Books.ID, + cds.ql`SELECT from bookshop.Books as $B { + $B.ID, ( - SELECT from bookshop.Books as Books2 { - Books2.ID, - } where Books2.ID = 1 + SELECT from bookshop.Books as $B2 { + $B2.ID, + } where $B2.ID = 1 ) as sub - } where Books.ID = 1`, + } where $B.ID = 1`, ) }) it('in a scoped subquery, always assign unique subquery aliases', () => { - const query = cds.ql`SELECT ID from bookshop.Item where exists (select ID from bookshop.Item:item)` + const query = cds.ql`SELECT ID from bookshop.Item where exists (select ID from bookshop.Item:Item)` const res = cqn4sql(query, model) const expected = cds.ql` - SELECT Item.ID from bookshop.Item as Item where exists ( - SELECT item2.ID from bookshop.Item as item2 where exists ( - SELECT 1 from bookshop.Item as Item3 where Item3.item_ID = item2.ID + SELECT $I.ID from bookshop.Item as $I where exists ( + SELECT $I2.ID from bookshop.Item as $I2 where exists ( + SELECT 1 from bookshop.Item as $I3 where $I3.Item_ID = $I2.ID ) ) ` @@ -734,30 +826,30 @@ describe('table alias access', () => { ID, ( SELECT from bookshop.Books { - Books.ID, - Books.author { + $B.ID, + $B.author { name }, - } where Books.author.dateOfBirth >= '01-01-1969' + } where $B.author.dateOfBirth >= '01-01-1969' ) as sub - } where Books.ID = 1 + } where $B.ID = 1 `, model, ) expect(JSON.parse(JSON.stringify(query))).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books { - Books.ID, + cds.ql`SELECT from bookshop.Books as $B { + $B.ID, ( - SELECT from bookshop.Books as Books2 - left join bookshop.Authors as author on author.ID = Books2.author_ID + SELECT from bookshop.Books as $B2 + left join bookshop.Authors as author on author.ID = $B2.author_ID { - Books2.ID, + $B2.ID, ( - SELECT author2.name from bookshop.Authors as author2 where Books2.author_ID = author2.ID + SELECT $a.name from bookshop.Authors as $a where $B2.author_ID = $a.ID ) as author } where author.dateOfBirth >= '01-01-1969' ) as sub - } where Books.ID = 1`, + } where $B.ID = 1`, ) }) it('in join relevant columns', () => { @@ -766,26 +858,26 @@ describe('table alias access', () => { ID, ( SELECT from bookshop.Books { - Books.ID, - Books.author.name, - } where Books.author.dateOfBirth >= '01-01-1969' + $B.ID, + $B.author.name, + } where $B.author.dateOfBirth >= '01-01-1969' ) as sub - } where Books.ID = 1 + } where $B.ID = 1 `, model, ) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books { - Books.ID, + cds.ql`SELECT from bookshop.Books as $B { + $B.ID, ( - SELECT from bookshop.Books as Books2 - left join bookshop.Authors as author on author.ID = Books2.author_ID + SELECT from bookshop.Books as $B2 + left join bookshop.Authors as author on author.ID = $B2.author_ID { - Books2.ID, + $B2.ID, author.name as author_name, } where author.dateOfBirth >= '01-01-1969' ) as sub - } where Books.ID = 1`, + } where $B.ID = 1`, ) }) it('in group by and order by', () => { @@ -794,26 +886,26 @@ describe('table alias access', () => { ID, ( SELECT from bookshop.Books { - Books.ID, + $B.ID, } - group by Books.title - order by Books.ID + group by $B.title + order by $B.ID ) as sub - } where Books.ID = 1 + } where $B.ID = 1 `, model, ) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books { - Books.ID, + cds.ql`SELECT from bookshop.Books as $B { + $B.ID, ( - SELECT from bookshop.Books as Books2 { - Books2.ID, + SELECT from bookshop.Books as $B2 { + $B2.ID, } - group by Books2.title - order by Books2.ID + group by $B2.title + order by $B2.ID ) as sub - } where Books.ID = 1`, + } where $B.ID = 1`, ) }) }) @@ -830,10 +922,10 @@ describe('table alias access', () => { }`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { - Books.stock * Books.price as foo, - power(Books.price, Books.stock) as bar, - Books.stock * power(sin(2*Books.price), 2*(Books.stock+3*Books.stock)) as nested, + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { + $B.stock * $B.price as foo, + power($B.price, $B.stock) as bar, + $B.stock * power(sin(2*$B.price), 2*($B.stock+3*$B.stock)) as nested, 2 as two }`) }) @@ -844,8 +936,8 @@ describe('table alias access', () => { where stock * price < power(price, stock) or stock * power(sin(2*price), 2*(stock+3*stock)) < 7`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } - where Books.stock * Books.price < power(Books.price, Books.stock) or Books.stock * power(sin(2*Books.price), 2*(Books.stock+3*Books.stock)) < 7`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID } + where $B.stock * $B.price < power($B.price, $B.stock) or $B.stock * power(sin(2*$B.price), 2*($B.stock+3*$B.stock)) < 7`) }) it('expressions and functions in GROUP BY/HAVING', () => { @@ -855,9 +947,9 @@ describe('table alias access', () => { having stock * price < power(price, stock) or stock * power(sin(2*price), 2*(stock+3*stock)) < 7`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } - group by Books.stock * Books.price, power(Books.price, Books.stock), Books.stock * power(sin(2*Books.price), 2*(Books.stock+3*Books.stock)) - having Books.stock * Books.price < power(Books.price, Books.stock) or Books.stock * power(sin(2*Books.price), 2*(Books.stock+3*Books.stock)) < 7`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID } + group by $B.stock * $B.price, power($B.price, $B.stock), $B.stock * power(sin(2*$B.price), 2*($B.stock+3*$B.stock)) + having $B.stock * $B.price < power($B.price, $B.stock) or $B.stock * power(sin(2*$B.price), 2*($B.stock+3*$B.stock)) < 7`) }) }) @@ -876,10 +968,11 @@ describe('table alias access', () => { }`, model, ) - expect(JSON.parse(JSON.stringify(query))).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { - Books.ID, - (SELECT from bookshop.Books as Books2 { Books2.author_ID, - (SELECT from bookshop.Books as Books3 { Books3.author_ID }) as bar + expect(JSON.parse(JSON.stringify(query))).to.deep.equal(cds.ql` + SELECT from bookshop.Books as $B { + $B.ID, + (SELECT from bookshop.Books as $B2 { $B2.author_ID, + (SELECT from bookshop.Books as $B3 { $B3.author_ID }) as bar }) as foo }`) }) @@ -899,15 +992,16 @@ describe('table alias access', () => { }`, model, ) - expect(JSON.parse(JSON.stringify(query))).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { - Books.ID, - (SELECT from bookshop.Books as Books2 { Books2.author_ID, - (SELECT from bookshop.Books as Books3 { - (SELECT from bookshop.Authors as author { - (SELECT from bookshop.Books as books4 { - books4.ID - } where author.ID = books4.author_ID) as books - } where Books3.author_ID = author.ID) as author + expect(JSON.parse(JSON.stringify(query))).to.deep.equal(cds.ql` + SELECT from bookshop.Books as $B { + $B.ID, + (SELECT from bookshop.Books as $B2 { $B2.author_ID, + (SELECT from bookshop.Books as $B3 { + (SELECT from bookshop.Authors as $a { + (SELECT from bookshop.Books as $b4 { + $b4.ID + } where $a.ID = $b4.author_ID) as books + } where $B3.author_ID = $a.ID) as author }) as bar }) as foo }`) @@ -916,17 +1010,17 @@ describe('table alias access', () => { // could maybe be relaxed later it('applies the same alias handling in subqueries in FROM', () => { let query = cqn4sql( - cds.ql`SELECT from (SELECT from bookshop.Books { ID, Books.stock }) as Books { ID, Books.stock }`, + cds.ql`SELECT from (SELECT from bookshop.Books { ID, stock }) as Books { ID, Books.stock }`, model, ) expect(query).to.deep.equal( - cds.ql`SELECT from (SELECT from bookshop.Books as Books2 { Books2.ID, Books2.stock }) as Books { Books.ID, Books.stock }`, + cds.ql`SELECT from (SELECT from bookshop.Books as $B { $B.ID, $B.stock }) as Books { Books.ID, Books.stock }`, ) }) it('explicit alias for FROM subquery', () => { let query = cqn4sql( cds.ql`SELECT from ( - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { ID, Books.stock, Books.dedication }) as B { ID, B.stock, B.dedication }`, model, @@ -952,7 +1046,7 @@ describe('table alias access', () => { ) }) it('wildcard expansion of subquery in from ignores assocs', () => { - let query = cqn4sql(cds.ql`SELECT from ( SELECT from bookshop.Orders ) as O`, model) + let query = cqn4sql(cds.ql`SELECT from ( SELECT from bookshop.Orders as Orders) as O`, model) expect(query).to.deep.equal( cds.ql`SELECT from ( SELECT from bookshop.Orders as Orders { @@ -965,7 +1059,7 @@ describe('table alias access', () => { }) it('prepends unique alias for function args or expressions on top of anonymous subquery', () => { let query = cqn4sql( - cds.ql`SELECT from ( SELECT from bookshop.Orders ) { + cds.ql`SELECT from ( SELECT from bookshop.Orders as Orders ) { sum(ID) as foo, ID + 42 as anotherFoo }`, @@ -986,7 +1080,7 @@ describe('table alias access', () => { // REVISIT: order not stable, move "ID" to top of columns in subquery in from let query = cqn4sql( cds.ql`SELECT from ( - SELECT from bookshop.Books { + SELECT from bookshop.Books as Books { sum(stock) as totalStock, ID, Books.stock, @@ -1023,13 +1117,13 @@ describe('table alias access', () => { it('cannot access table name of FROM subquery in outer query', () => { expect(() => - cqn4sql(cds.ql`SELECT from (SELECT from bookshop.Books { ID, Books.stock }) as B { ID, Books.stock }`, model), + cqn4sql(cds.ql`SELECT from (SELECT from bookshop.Books as Books { ID, Books.stock }) as B { ID, Books.stock }`, model), ).to.throw(/"Books" not found in the elements of "B"/) }) it('expose column of inner query in outer query', () => { let query = cqn4sql( - cds.ql`SELECT from (SELECT from bookshop.Books { ID, Books.stock as Books }) as B { ID, Books }`, + cds.ql`SELECT from (SELECT from bookshop.Books as Books { ID, Books.stock as Books }) as B { ID, B.Books }`, model, ) expect(query).to.deep.equal( @@ -1049,16 +1143,16 @@ describe('table alias access', () => { it('applies the same alias handling in value subqueries', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID, (SELECT from bookshop.Books { ID }) as foo }`, model) expect(JSON.parse(JSON.stringify(query))).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books { - Books.ID, - (SELECT from bookshop.Books as Books2 { Books2.ID } ) as foo + cds.ql`SELECT from bookshop.Books as $B { + $B.ID, + (SELECT from bookshop.Books as $B2 { $B2.ID } ) as foo }`, ) }) it('supports correlated value subquery in select list', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, (SELECT from bookshop.Books as Q { ID } where Q.ID = Books.ID) as foo }`, + cds.ql`SELECT from bookshop.Books as Books { ID, (SELECT from bookshop.Books as Q { ID } where Q.ID = Books.ID) as foo }`, model, ) expect(JSON.parse(JSON.stringify(query))).to.deep.equal( @@ -1078,7 +1172,7 @@ describe('table alias access', () => { it('in correlated subquery, allows access to fields of inner query without explicit table alias', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID, (SELECT from bookshop.Books as Q { ID } where ID = Books.ID) as foo }`, + cds.ql`SELECT from bookshop.Books as Books { ID, (SELECT from bookshop.Books as Q { ID } where ID = Books.ID) as foo }`, model, ) expect(JSON.parse(JSON.stringify(query))).to.deep.equal( @@ -1138,8 +1232,8 @@ describe('table alias access', () => { }) it('in nested correlated subqueries, table alias may be shadowed', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { - (SELECT from bookshop.Authors { + cds.ql`SELECT from bookshop.Books as Books { + (SELECT from bookshop.Authors as Authors { books.title } where name = Books.title) as foo }`, @@ -1182,14 +1276,14 @@ describe('table alias access', () => { ], }).columns('ID') - const expected = SELECT.from('bookshop.Books as Books') - .columns('Books.ID') + const expected = SELECT.from('bookshop.Books as $B') + .columns('$B.ID') .where([ { - list: [{ ref: ['Books', 'dedication_addressee_ID'] }], + list: [{ ref: ['$B', 'dedication_addressee_ID'] }], }, 'in', - cds.ql`SELECT Books2.ID from bookshop.Books as Books2 where Books2.ID = 5`, + cds.ql`SELECT $B2.ID from bookshop.Books as $B2 where $B2.ID = 5`, ]) const res = cqn4sql(query, model) @@ -1209,22 +1303,23 @@ describe('table alias access', () => { }, 'coAuthorUnmanaged', ], + as: 'coAuthorUnmanaged', }).columns('ID') + const expected = SELECT.from('bookshop.Authors as coAuthorUnmanaged').columns('coAuthorUnmanaged.ID').where(` + exists ( + SELECT 1 from bookshop.Books as $B where coAuthorUnmanaged.ID = $B.coAuthor_ID_unmanaged + ) + `) + const list = [ { - list: [{ ref: ['Books', 'dedication_addressee_ID'] }], + list: [{ ref: ['$B', 'dedication_addressee_ID'] }], }, 'in', cds.ql`SELECT Books.ID from bookshop.Books as Books where Books.ID = 5`, ] - const expected = SELECT.from('bookshop.Authors as coAuthorUnmanaged').columns('coAuthorUnmanaged.ID').where(` - exists ( - SELECT 1 from bookshop.Books as Books where coAuthorUnmanaged.ID = Books.coAuthor_ID_unmanaged - ) - `) - expected.SELECT.where[1].SELECT.where.push('and', ...list) const res = cqn4sql(query, model) @@ -1233,7 +1328,7 @@ describe('table alias access', () => { it('handles value subquery in WHERE', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID } + cds.ql`SELECT from bookshop.Books as Books { ID } WHERE (SELECT from bookshop.Books as qInWhere { ID }) = 5`, model, ) @@ -1243,7 +1338,7 @@ describe('table alias access', () => { it('handles correlated value subquery in WHERE', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { ID } + cds.ql`SELECT from bookshop.Books as Books { ID } WHERE (SELECT from bookshop.Books as qInWhere { ID } where ID = Books.ID) = 5`, model, ) @@ -1253,8 +1348,8 @@ describe('table alias access', () => { it('handles EXISTS subquery in WHERE', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors { ID } WHERE exists ( - SELECT 1 from bookshop.Books where ID = Authors.ID + cds.ql`SELECT from bookshop.Authors as Authors { ID } WHERE exists ( + SELECT 1 from bookshop.Books as Books where ID = Authors.ID )`, model, ) @@ -1279,8 +1374,8 @@ describe('table alias access', () => { it('handles the select list of an exists subquery like any other select list', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors { ID } WHERE exists ( - SELECT ID, stock, price from bookshop.Books where ID = Authors.ID + cds.ql`SELECT from bookshop.Authors as Authors { ID } WHERE exists ( + SELECT ID, stock, price from bookshop.Books as Books where ID = Authors.ID )`, model, ) diff --git a/db-service/test/cqn4sql/tupleExpansion.test.js b/db-service/test/cqn4sql/tupleExpansion.test.js index 752e50262..5f6ca151f 100644 --- a/db-service/test/cqn4sql/tupleExpansion.test.js +++ b/db-service/test/cqn4sql/tupleExpansion.test.js @@ -23,7 +23,7 @@ describe('Structural comparison', () => { it('compare scalar leaf with value', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.Books { ID } where dedication.text ${first} 'for mommy'` + const queryString = `SELECT from bookshop.Books as Books { ID } where dedication.text ${first} 'for mommy'` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.Books as Books { Books.ID } @@ -34,7 +34,7 @@ describe('Structural comparison', () => { it('compare scalar (join relevant) leaf with value', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.Books { ID } where dedication.addressee.name ${first} 'mommy'` + const queryString = `SELECT from bookshop.Books as Books { ID } where dedication.addressee.name ${first} 'mommy'` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.Books as Books @@ -48,7 +48,7 @@ describe('Structural comparison', () => { it('expand struct1 struct2 in where w/ parens', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.EStruc { ID } where not struc1 ${first} struc2` + const queryString = `SELECT from bookshop.EStruc as EStruc { ID } where not struc1 ${first} struc2` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.EStruc as EStruc { EStruc.ID } @@ -62,7 +62,7 @@ describe('Structural comparison', () => { it('expand assoc1 assoc2 in where w/ parens', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.Books { ID } where not author ${first} coAuthor` + const queryString = `SELECT from bookshop.Books as Books { ID } where not author ${first} coAuthor` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.Books as Books { Books.ID } @@ -74,7 +74,7 @@ describe('Structural comparison', () => { it('expand struct1 struct2 in where w/o parens', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.EStruc { ID } where struc1 ${first} struc2` + const queryString = `SELECT from bookshop.EStruc as EStruc { ID } where struc1 ${first} struc2` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.EStruc as EStruc { EStruc.ID } @@ -87,7 +87,7 @@ describe('Structural comparison', () => { it('expand assoc1 assoc2 in where w/o parens', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.Books { ID } where author ${first} coAuthor` + const queryString = `SELECT from bookshop.Books as Books { ID } where author ${first} coAuthor` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.Books as Books { Books.ID } @@ -99,7 +99,7 @@ describe('Structural comparison', () => { it('expand struct1 struct2 in where w/o parens', () => { notEqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.EStruc { ID } where struc1 ${first} struc2` + const queryString = `SELECT from bookshop.EStruc as EStruc { ID } where struc1 ${first} struc2` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.EStruc as EStruc { EStruc.ID } @@ -115,8 +115,8 @@ describe('Structural comparison', () => { const queryString = `SELECT from bookshop.Books { ID } where author ${first} coAuthor` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` - SELECT from bookshop.Books as Books { Books.ID } - where Books.author_ID ${first} Books.coAuthor_ID` + SELECT from bookshop.Books as $B { $B.ID } + where $B.author_ID ${first} $B.coAuthor_ID` expect(query).to.deep.equal(CQL(expectedQueryString)) }) }) @@ -124,7 +124,7 @@ describe('Structural comparison', () => { it('expand struct1 struct2 in where w/ parens', () => { notEqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.EStruc { ID } where struc1 ${first} struc2 and 1 = 1` + const queryString = `SELECT from bookshop.EStruc as EStruc { ID } where struc1 ${first} struc2 and 1 = 1` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.EStruc as EStruc { EStruc.ID } @@ -137,7 +137,7 @@ describe('Structural comparison', () => { it('expand assoc1 assoc2 in where w/ parens', () => { notEqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.Books { ID } where author ${first} coAuthor and 1 = 1` + const queryString = `SELECT from bookshop.Books as Books { ID } where author ${first} coAuthor and 1 = 1` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.Books as Books { Books.ID } @@ -149,7 +149,7 @@ describe('Structural comparison', () => { it('expand struc2Reversed struct2 in where w/o parens (order should not matter)', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.EStruc { ID } where struc2Reversed ${first} struc2` + const queryString = `SELECT from bookshop.EStruc as EStruc { ID } where struc2Reversed ${first} struc2` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.EStruc as EStruc { EStruc.ID } @@ -161,7 +161,7 @@ describe('Structural comparison', () => { it('compare nested struct', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.Foo { ID } where stru.nested ${first} stru.nested` + const queryString = `SELECT from bookshop.Foo as Foo { ID } where stru.nested ${first} stru.nested` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.Foo as Foo { Foo.ID } @@ -236,7 +236,7 @@ describe('Structural comparison', () => { it('compare struct with struct via assoc', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.EStrucSibling { ID } + const queryString = `SELECT from bookshop.EStrucSibling as EStrucSibling { ID } where EStrucSibling.struc2 ${first} EStrucSibling.self.struc2` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` @@ -255,8 +255,8 @@ describe('Structural comparison', () => { const queryString = `SELECT from bookshop.DeepRecursiveAssoc { ID } where one ${first} one` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` - SELECT from bookshop.DeepRecursiveAssoc as DeepRecursiveAssoc { DeepRecursiveAssoc.ID } - where DeepRecursiveAssoc.one_two_three_toSelf_ID ${first} DeepRecursiveAssoc.one_two_three_toSelf_ID` + SELECT from bookshop.DeepRecursiveAssoc as $D { $D.ID } + where $D.one_two_three_toSelf_ID ${first} $D.one_two_three_toSelf_ID` expect(query).to.deep.equal(CQL(expectedQueryString)) }) }) @@ -265,14 +265,14 @@ describe('Structural comparison', () => { const queryString = `SELECT from bookshop.AssocWithStructuredKey[toStructuredKey = null]:accessGroup { ID }` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` - SELECT from bookshop.AccessGroups as accessGroup - { accessGroup.ID } + SELECT from bookshop.AccessGroups as $a + { $a.ID } where exists ( - SELECT 1 from bookshop.AssocWithStructuredKey as AssocWithStructuredKey - where AssocWithStructuredKey.accessGroup_ID = accessGroup.ID and - AssocWithStructuredKey.toStructuredKey_struct_mid_leaf = null and - AssocWithStructuredKey.toStructuredKey_struct_mid_anotherLeaf = null and - AssocWithStructuredKey.toStructuredKey_second = null + SELECT 1 from bookshop.AssocWithStructuredKey as $A2 + where $A2.accessGroup_ID = $a.ID and + $A2.toStructuredKey_struct_mid_leaf = null and + $A2.toStructuredKey_struct_mid_anotherLeaf = null and + $A2.toStructuredKey_second = null ) ` expect(query).to.deep.equal(CQL(expectedQueryString)) @@ -322,7 +322,7 @@ describe('Structural comparison', () => { it('join relevant structural comparison', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.EStrucSibling { ID } where not struc2 ${first} sibling.struc2` + const queryString = `SELECT from bookshop.EStrucSibling as EStrucSibling { ID } where not struc2 ${first} sibling.struc2` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.EStrucSibling as EStrucSibling @@ -336,7 +336,7 @@ describe('Structural comparison', () => { it('join relevant structural comparison / both operands are in assoc target', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.EStrucSibling { ID } where not sibling.struc2 ${first} sibling.struc2` + const queryString = `SELECT from bookshop.EStrucSibling as EStrucSibling { ID } where not sibling.struc2 ${first} sibling.struc2` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.EStrucSibling as EStrucSibling @@ -350,7 +350,7 @@ describe('Structural comparison', () => { it('join relevant structural comparison / both operands are in assoc target / not equal ops', () => { notEqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.EStrucSibling { ID } where not sibling.struc2 ${first} sibling.struc2` + const queryString = `SELECT from bookshop.EStrucSibling as EStrucSibling { ID } where not sibling.struc2 ${first} sibling.struc2` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.EStrucSibling as EStrucSibling @@ -364,7 +364,7 @@ describe('Structural comparison', () => { it('join relevant structural comparison / both operands are in assoc target which is the source entity', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.EStrucSibling { ID } where not self.struc2 ${first} self.struc2` + const queryString = `SELECT from bookshop.EStrucSibling as EStrucSibling { ID } where not self.struc2 ${first} self.struc2` let query = cqn4sql(CQL(queryString), model) const expectedQueryString = ` SELECT from bookshop.EStrucSibling as EStrucSibling @@ -387,7 +387,7 @@ describe('Structural comparison', () => { it('proper error for comparison w/ value', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.AssocWithStructuredKey { ID } where not AssocWithStructuredKey.toStructuredKey ${first} 5` + const queryString = `SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { ID } where not AssocWithStructuredKey.toStructuredKey ${first} 5` expect(() => cqn4sql(CQL(queryString), model)).to.throw( 'Can\'t compare structure "AssocWithStructuredKey.toStructuredKey" with value "5"', ) @@ -396,14 +396,14 @@ describe('Structural comparison', () => { it('proper error for comparison w/ value, reversed', () => { eqOps.forEach(op => { const [first] = op - const queryString = `SELECT from bookshop.AssocWithStructuredKey { ID } where not 5 ${first} AssocWithStructuredKey.toStructuredKey` + const queryString = `SELECT from bookshop.AssocWithStructuredKey as AssocWithStructuredKey { ID } where not 5 ${first} AssocWithStructuredKey.toStructuredKey` expect(() => cqn4sql(CQL(queryString), model)).to.throw( "An association can't be used as a value in an expression", ) }) }) it('Struct needs to be unfolded in on-condition of join', () => { - const query = cds.ql`SELECT from bookshop.Unmanaged { + const query = cds.ql`SELECT from bookshop.Unmanaged as Unmanaged { toSelf.field }` diff --git a/db-service/test/cqn4sql/where-exists.test.js b/db-service/test/cqn4sql/where-exists.test.js index 980719134..41a871844 100644 --- a/db-service/test/cqn4sql/where-exists.test.js +++ b/db-service/test/cqn4sql/where-exists.test.js @@ -16,8 +16,8 @@ describe('EXISTS predicate in where', () => { describe('access association after `exists` predicate', () => { it('exists predicate for to-many assoc w/o alias', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } where exists author`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID )`) }) it('exists predicate after having', () => { @@ -25,10 +25,10 @@ describe('EXISTS predicate in where', () => { // having only works on aggregated queries, hence the "group by" to make // the example more "real life" expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books { Books.ID } - GROUP BY Books.ID + cds.ql`SELECT from bookshop.Books as $B { $B.ID } + GROUP BY $B.ID HAVING EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID )`, ) }) @@ -37,10 +37,10 @@ describe('EXISTS predicate in where', () => { // having only works on aggregated queries, hence the "group by" to make // the example more "real life" expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books { Books.ID } - GROUP BY Books.ID + cds.ql`SELECT from bookshop.Books as $B { $B.ID } + GROUP BY $B.ID HAVING EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID and author.ID = 42 + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID and $a.ID = 42 )`, ) }) @@ -49,14 +49,14 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Books { ID } where exists genre.children[code = 'ABC'] or exists genre.children[code = 'DEF']`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Genres as genre where genre.ID = Books.genre_ID - and EXISTS ( SELECT 1 from bookshop.Genres as children where children.parent_ID = genre.ID and children.code = 'ABC' ) + SELECT 1 from bookshop.Genres as $g where $g.ID = $B.genre_ID + and EXISTS ( SELECT 1 from bookshop.Genres as $c where $c.parent_ID = $g.ID and $c.code = 'ABC' ) ) or EXISTS ( - SELECT 1 from bookshop.Genres as genre2 where genre2.ID = Books.genre_ID - and EXISTS ( SELECT 1 from bookshop.Genres as children2 where children2.parent_ID = genre2.ID and children2.code = 'DEF' ) + SELECT 1 from bookshop.Genres as $g2 where $g2.ID = $B.genre_ID + and EXISTS ( SELECT 1 from bookshop.Genres as $c2 where $c2.parent_ID = $g2.ID and $c2.code = 'DEF' ) )`) }) it('exists predicate for assoc combined with path expression in xpr', () => { @@ -65,27 +65,27 @@ describe('EXISTS predicate in where', () => { model, ) expect(query).to.deep.equal(cds.ql` - SELECT from bookshop.Books as Books - left join bookshop.Authors as author on author.ID = Books.author_ID + SELECT from bookshop.Books as $B + left join bookshop.Authors as author on author.ID = $B.author_ID { - Books.ID + $B.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Authors as author2 where author2.ID = Books.author_ID + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID ) and ((author.name + 's') = 'Schillers')`) }) it('handles simple where exists with implicit table alias', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } where exists Books.author`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { ID } where exists Books.author`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID + SELECT 1 from bookshop.Authors as $a where $a.ID = Books.author_ID )`) }) it('handles simple where exists with explicit table alias', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS Authors.books`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Authors as Authors { ID } WHERE EXISTS Authors.books`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID + SELECT 1 from bookshop.Books as $b where $b.author_ID = Authors.ID )`) }) // @@ -94,44 +94,44 @@ describe('EXISTS predicate in where', () => { // it('exists predicate for to-many assoc', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID )`) }) it('FROM clause has explicit table alias', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors as A { ID } WHERE EXISTS books`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as A { A.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = A.ID + SELECT 1 from bookshop.Books as $b where $b.author_ID = A.ID )`) }) it('using explicit table alias of FROM clause', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors as A { ID } WHERE EXISTS A.books`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as A { A.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = A.ID + SELECT 1 from bookshop.Books as $b where $b.author_ID = A.ID )`) }) it('FROM clause has table alias with the same name as the assoc', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors as books { ID } WHERE EXISTS books`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as books { books.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = books.ID + SELECT 1 from bookshop.Books as $b where $b.author_ID = books.ID )`) }) it('using the mean table alias of the FROM clause to access the association', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors as books { ID } WHERE EXISTS books.books`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as books { books.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = books.ID + SELECT 1 from bookshop.Books as $b where $b.author_ID = books.ID )`) }) it('exists predicate has additional condition', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors { ID } WHERE exists books and name = 'Horst'`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } - WHERE exists ( select 1 from bookshop.Books as books where books.author_ID = Authors.ID ) - AND Authors.name = 'Horst' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } + WHERE exists ( select 1 from bookshop.Books as $b where $b.author_ID = $A.ID ) + AND $A.name = 'Horst' `) }) it('exists predicate is followed by association-like calculated element', () => { @@ -139,9 +139,9 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Authors { ID } WHERE exists booksWithALotInStock and name = 'Horst'`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } - WHERE exists ( select 1 from bookshop.Books as booksWithALotInStock where ( booksWithALotInStock.author_ID = Authors.ID ) and ( booksWithALotInStock.stock > 100 ) ) - AND Authors.name = 'Horst' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } + WHERE exists ( select 1 from bookshop.Books as $b where ( $b.author_ID = $A.ID ) and ( $b.stock > 100 ) ) + AND $A.name = 'Horst' `) }) }) @@ -151,10 +151,10 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Books { ID } where ( ( exists author[name = 'Schiller'] ) + 2 ) = 'foo'`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID } WHERE ( ( - EXISTS ( SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID and author.name = 'Schiller' ) + EXISTS ( SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID and $a.name = 'Schiller' ) ) + 2 ) = 'foo'`) }) @@ -165,10 +165,10 @@ describe('EXISTS predicate in where', () => { query.SELECT.where[1].ref[0].where = [{ xpr: [...query.SELECT.where[1].ref[0].where] }] const res = cqn4sql(query, model) const expected = cds.ql` - SELECT from bookshop.Authors as Authors { Authors.ID } where exists ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID + SELECT from bookshop.Authors as $A { $A.ID } where exists ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID and exists ( - SELECT 1 from bookshop.Genres as genre where genre.ID = books.genre_ID and genre.parent_ID = 1 + SELECT 1 from bookshop.Genres as $g where $g.ID = $b.genre_ID and $g.parent_ID = 1 ) )` // cannot be expressed with the template string cds.ql`` builder @@ -183,22 +183,22 @@ describe('EXISTS predicate in where', () => { it('where exists to-one association with additional filter', () => { // note: now all source side elements are addressed with their table alias let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } where exists author[name = 'Sanderson']`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID and author.name = 'Sanderson' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID and $a.name = 'Sanderson' )`) }) it('where exists to-one association with additional filter with xpr', () => { // note: now all source side elements are addressed with their table alias let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } where exists author[not (name = 'Sanderson')]`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID and not (author.name = 'Sanderson') + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID and not ($a.name = 'Sanderson') )`) }) it('MUST ... with simple filter', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books[title = 'ABAP Objects']`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND books.title = 'ABAP Objects' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND $b.title = 'ABAP Objects' )`) }) @@ -223,8 +223,8 @@ describe('EXISTS predicate in where', () => { model, ) // TODO original test had no before `dedication_text` - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND books.dedication_text = 'For Hasso' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND $b.dedication_text = 'For Hasso' )`) }) @@ -234,8 +234,8 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books[dedication.addressee.ID = 29]`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND books.dedication_addressee_ID = 29 + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND $b.dedication_addressee_ID = 29 )`) }) @@ -261,17 +261,17 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Books { ID } where exists author.books[title = 'Harry Potter']`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID and EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = author.ID and books2.title = 'Harry Potter' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID and EXISTS ( + SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a.ID and $b2.title = 'Harry Potter' ) )`) }) it('MUST handle simple where exists with additional filter, shortcut notation', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } where exists author[17]`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID and author.ID = 17 + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID and $a.ID = 17 )`) }) }) @@ -282,9 +282,9 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Books { ID } where exists author[exists books[title = 'Harry Potter']]`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID and EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = author.ID and books2.title = 'Harry Potter' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { $B.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID and EXISTS ( + SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a.ID and $b2.title = 'Harry Potter' ) )`) }) @@ -301,15 +301,15 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books[EXISTS author or title = 'Gravity']`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND ( EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = books.author_ID - ) or books.title = 'Gravity' + SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID + ) or $b.title = 'Gravity' ) )`) }) @@ -318,18 +318,18 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books[EXISTS coAuthorUnmanaged[EXISTS books]]`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND EXISTS ( - SELECT 1 from bookshop.Authors as coAuthorUnmanaged - where coAuthorUnmanaged.ID = books.coAuthor_ID_unmanaged AND + SELECT 1 from bookshop.Authors as $c + where $c.ID = $b.coAuthor_ID_unmanaged AND EXISTS ( - SELECT 1 from bookshop.Books as books2 where - books2.author_ID = coAuthorUnmanaged.ID + SELECT 1 from bookshop.Books as $b2 where + $b2.author_ID = $c.ID ) ) )`) @@ -337,9 +337,9 @@ describe('EXISTS predicate in where', () => { it('MUST ... EXISTS with nested assoc', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books { ID } WHERE EXISTS dedication.addressee`, model) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books { Books.ID } + cds.ql`SELECT from bookshop.Books as $B { $B.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Person as addressee where addressee.ID = Books.dedication_addressee_ID + SELECT 1 from bookshop.Person as $a where $a.ID = $B.dedication_addressee_ID )`, ) }) @@ -349,13 +349,13 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books[title = 'Gravity' or EXISTS author]`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where - books.author_ID = Authors.ID AND - ( books.title = 'Gravity' or + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where + $b.author_ID = $A.ID AND + ( $b.title = 'Gravity' or EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = books.author_ID + SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID ) ) )`) @@ -366,10 +366,10 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books[NOT EXISTS author[EXISTS books]]`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND NOT EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = books.author_ID AND EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = author.ID + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND NOT EXISTS ( + SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID AND EXISTS ( + SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a2.ID ) ) )`) @@ -383,11 +383,11 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books[EXISTS author or title = 'Gravity'].genre[name = 'Fiction']`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND ( EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = books.author_ID - ) or books.title = 'Gravity' ) AND EXISTS ( - SELECT 1 from bookshop.Genres as genre where genre.ID = books.genre_ID and genre.name = 'Fiction' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND ( EXISTS ( + SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID + ) or $b.title = 'Gravity' ) AND EXISTS ( + SELECT 1 from bookshop.Genres as $g where $g.ID = $b.genre_ID and $g.name = 'Fiction' ) )`) }) @@ -400,12 +400,12 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books[EXISTS author or title = 'Gravity'].genre[name = 'Fiction' and exists children[name = 'Foo']]`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND ( EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = books.author_ID - ) or books.title = 'Gravity') AND EXISTS ( - SELECT 1 from bookshop.Genres as genre where genre.ID = books.genre_ID AND genre.name = 'Fiction' AND EXISTS ( - SELECT 1 from bookshop.Genres as children where children.parent_ID = genre.ID AND children.name = 'Foo' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND ( EXISTS ( + SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID + ) or $b.title = 'Gravity') AND EXISTS ( + SELECT 1 from bookshop.Genres as $g where $g.ID = $b.genre_ID AND $g.name = 'Fiction' AND EXISTS ( + SELECT 1 from bookshop.Genres as $c where $c.parent_ID = $g.ID AND $c.name = 'Foo' ) ) )`) @@ -418,20 +418,20 @@ describe('EXISTS predicate in where', () => { // it('MUST ... with 2 assocs', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books.author`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = books.author_ID + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND EXISTS ( + SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID ) )`) }) it('MUST ... with 4 assocs', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books.author.books.author`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = books.author_ID AND EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = author.ID AND EXISTS ( - SELECT 1 from bookshop.Authors as author2 where author2.ID = books2.author_ID + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND EXISTS ( + SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID AND EXISTS ( + SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a2.ID AND EXISTS ( + SELECT 1 from bookshop.Authors as $a3 where $a3.ID = $b2.author_ID ) ) ) @@ -443,19 +443,19 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books.author.books.author AND EXISTS books.author.books.author`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = books.author_ID AND EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = author.ID AND EXISTS ( - SELECT 1 from bookshop.Authors as author2 where author2.ID = books2.author_ID + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND EXISTS ( + SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID AND EXISTS ( + SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a2.ID AND EXISTS ( + SELECT 1 from bookshop.Authors as $a3 where $a3.ID = $b2.author_ID ) ) ) ) AND EXISTS ( - SELECT 1 from bookshop.Books as books3 where books3.author_ID = Authors.ID AND EXISTS ( - SELECT 1 from bookshop.Authors as author3 where author3.ID = books3.author_ID AND EXISTS ( - SELECT 1 from bookshop.Books as books4 where books4.author_ID = author3.ID AND EXISTS ( - SELECT 1 from bookshop.Authors as author4 where author4.ID = books4.author_ID + SELECT 1 from bookshop.Books as $b3 where $b3.author_ID = $A.ID AND EXISTS ( + SELECT 1 from bookshop.Authors as $a4 where $a4.ID = $b3.author_ID AND EXISTS ( + SELECT 1 from bookshop.Books as $b4 where $b4.author_ID = $a4.ID AND EXISTS ( + SELECT 1 from bookshop.Authors as $a5 where $a5.ID = $b4.author_ID ) ) ) @@ -490,11 +490,11 @@ describe('EXISTS predicate in where', () => { cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books[stock > 11].author[name = 'Horst'].books[price < 9.99].author[placeOfBirth = 'Rom']`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID AND books.stock > 11 AND EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = books.author_ID AND author.name = 'Horst' AND EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = author.ID AND books2.price < 9.99 AND EXISTS ( - SELECT 1 from bookshop.Authors as author2 where author2.ID = books2.author_ID AND author2.placeOfBirth = 'Rom' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A { $A.ID } WHERE EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID AND $b.stock > 11 AND EXISTS ( + SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID AND $a2.name = 'Horst' AND EXISTS ( + SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a2.ID AND $b2.price < 9.99 AND EXISTS ( + SELECT 1 from bookshop.Authors as $a3 where $a3.ID = $b2.author_ID AND $a3.placeOfBirth = 'Rom' ) ) ) @@ -520,9 +520,9 @@ describe('EXISTS predicate in where', () => { }`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { - Books.ID, - case when exists (SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID) then 'yes' + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { + $B.ID, + case when exists (SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID) then 'yes' else 'no' end as x }`) @@ -538,11 +538,11 @@ describe('EXISTS predicate in where', () => { }`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { - Books.ID, + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as $B { + $B.ID, case when exists ( - SELECT 1 from bookshop.Authors as author where author.ID = Books.author_ID and author.name = 'Sanderson' + SELECT 1 from bookshop.Authors as $a where $a.ID = $B.author_ID and $a.name = 'Sanderson' ) then 'yes' else 'no' end as x @@ -559,16 +559,16 @@ describe('EXISTS predicate in where', () => { }`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors - { Authors.ID, + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A + { $A.ID, case when exists ( - select 1 from bookshop.Books as books where books.author_ID = Authors.ID and books.price > 10 + select 1 from bookshop.Books as $b where $b.author_ID = $A.ID and $b.price > 10 ) then 1 when exists ( - select 1 from bookshop.Books as books2 where books2.author_ID = Authors.ID and books2.price > 100 + select 1 from bookshop.Books as $b2 where $b2.author_ID = $A.ID and $b2.price > 100 ) then 2 end as descr @@ -585,16 +585,16 @@ describe('EXISTS predicate in where', () => { }`, model, ) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Authors - { Authors.ID, + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $A + { $A.ID, case when exists ( - select 1 from bookshop.Books as booksWithALotInStock where ( booksWithALotInStock.author_ID = Authors.ID ) and ( booksWithALotInStock.stock > 100 ) and ( booksWithALotInStock.price > 10 or booksWithALotInStock.price < 20 ) + select 1 from bookshop.Books as $b where ( $b.author_ID = $A.ID ) and ( $b.stock > 100 ) and ( $b.price > 10 or $b.price < 20 ) ) then 1 when exists ( - select 1 from bookshop.Books as booksWithALotInStock2 where ( booksWithALotInStock2.author_ID = Authors.ID ) and ( booksWithALotInStock2.stock > 100 ) and ( booksWithALotInStock2.price > 100 or booksWithALotInStock2.price < 120 ) + select 1 from bookshop.Books as $b2 where ( $b2.author_ID = $A.ID ) and ( $b2.stock > 100 ) and ( $b2.price > 100 or $b2.price < 120 ) ) then 2 end as descr } @@ -613,73 +613,73 @@ describe('EXISTS predicate in where', () => { it('... managed association with structured FK', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1 as AM { ID } WHERE EXISTS a_struc`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze1 as AM { AM.ID } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze2 as a_struc where a_struc.ID_1_a = AM.a_struc_ID_1_a and a_struc.ID_1_b = AM.a_struc_ID_1_b - and a_struc.ID_2_a = AM.a_struc_ID_2_a and a_struc.ID_2_b = AM.a_struc_ID_2_b + SELECT 1 from bookshop.AssocMaze2 as $a where $a.ID_1_a = AM.a_struc_ID_1_a and $a.ID_1_b = AM.a_struc_ID_1_b + and $a.ID_2_a = AM.a_struc_ID_2_a and $a.ID_2_b = AM.a_struc_ID_2_b )`) }) it('... managed association with explicit simple FKs', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1 as AM { ID } where exists a_strucX`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze1 as AM { AM.ID } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze2 as a_strucX where a_strucX.a = AM.a_strucX_a and a_strucX.b = AM.a_strucX_b + SELECT 1 from bookshop.AssocMaze2 as $a where $a.a = AM.a_strucX_a and $a.b = AM.a_strucX_b )`) }) it('... managed association with explicit structured FKs', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1 as AM { ID } where exists a_strucY`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze1 as AM { AM.ID } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze2 as a_strucY where a_strucY.S_1_a = AM.a_strucY_S_1_a and a_strucY.S_1_b = AM.a_strucY_S_1_b - and a_strucY.S_2_a = AM.a_strucY_S_2_a and a_strucY.S_2_b = AM.a_strucY_S_2_b + SELECT 1 from bookshop.AssocMaze2 as $a where $a.S_1_a = AM.a_strucY_S_1_a and $a.S_1_b = AM.a_strucY_S_1_b + and $a.S_2_a = AM.a_strucY_S_2_a and $a.S_2_b = AM.a_strucY_S_2_b )`) }) it('... managed association with explicit structured aliased FKs', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1 as AM { ID } where exists a_strucXA`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze1 as AM { AM.ID } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze2 as a_strucXA where a_strucXA.S_1_a = AM.a_strucXA_T_1_a and a_strucXA.S_1_b = AM.a_strucXA_T_1_b - and a_strucXA.S_2_a = AM.a_strucXA_T_2_a and a_strucXA.S_2_b = AM.a_strucXA_T_2_b + SELECT 1 from bookshop.AssocMaze2 as $a where $a.S_1_a = AM.a_strucXA_T_1_a and $a.S_1_b = AM.a_strucXA_T_1_b + and $a.S_2_a = AM.a_strucXA_T_2_a and $a.S_2_b = AM.a_strucXA_T_2_b )`) }) it('... managed associations with FKs being managed associations', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1 as AM { ID } where exists a_assoc`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze1 as AM { AM.ID } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze3 as a_assoc where a_assoc.assoc1_ID_1_a = AM.a_assoc_assoc1_ID_1_a and a_assoc.assoc1_ID_1_b = AM.a_assoc_assoc1_ID_1_b - and a_assoc.assoc1_ID_2_a = AM.a_assoc_assoc1_ID_2_a and a_assoc.assoc1_ID_2_b = AM.a_assoc_assoc1_ID_2_b - and a_assoc.assoc2_ID_1_a = AM.a_assoc_assoc2_ID_1_a and a_assoc.assoc2_ID_1_b = AM.a_assoc_assoc2_ID_1_b - and a_assoc.assoc2_ID_2_a = AM.a_assoc_assoc2_ID_2_a and a_assoc.assoc2_ID_2_b = AM.a_assoc_assoc2_ID_2_b + SELECT 1 from bookshop.AssocMaze3 as $a where $a.assoc1_ID_1_a = AM.a_assoc_assoc1_ID_1_a and $a.assoc1_ID_1_b = AM.a_assoc_assoc1_ID_1_b + and $a.assoc1_ID_2_a = AM.a_assoc_assoc1_ID_2_a and $a.assoc1_ID_2_b = AM.a_assoc_assoc1_ID_2_b + and $a.assoc2_ID_1_a = AM.a_assoc_assoc2_ID_1_a and $a.assoc2_ID_1_b = AM.a_assoc_assoc2_ID_1_b + and $a.assoc2_ID_2_a = AM.a_assoc_assoc2_ID_2_a and $a.assoc2_ID_2_b = AM.a_assoc_assoc2_ID_2_b )`) }) it('... managed association with explicit FKs being managed associations', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1 as AM { ID } where exists a_assocY`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze1 as AM { AM.ID } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze2 as a_assocY where a_assocY.A_1_a = AM.a_assocY_A_1_a and a_assocY.A_1_b_ID = AM.a_assocY_A_1_b_ID - and a_assocY.A_2_a = AM.a_assocY_A_2_a and a_assocY.A_2_b_ID = AM.a_assocY_A_2_b_ID + SELECT 1 from bookshop.AssocMaze2 as $a where $a.A_1_a = AM.a_assocY_A_1_a and $a.A_1_b_ID = AM.a_assocY_A_1_b_ID + and $a.A_2_a = AM.a_assocY_A_2_a and $a.A_2_b_ID = AM.a_assocY_A_2_b_ID )`) }) it('... managed association with explicit aliased FKs being managed associations', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1 as AM { ID } where exists a_assocYA`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze1 as AM { AM.ID } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze2 as a_assocYA where a_assocYA.A_1_a = AM.a_assocYA_B_1_a and a_assocYA.A_1_b_ID = AM.a_assocYA_B_1_b_ID - and a_assocYA.A_2_a = AM.a_assocYA_B_2_a and a_assocYA.A_2_b_ID = AM.a_assocYA_B_2_b_ID + SELECT 1 from bookshop.AssocMaze2 as $a where $a.A_1_a = AM.a_assocYA_B_1_a and $a.A_1_b_ID = AM.a_assocYA_B_1_b_ID + and $a.A_2_a = AM.a_assocYA_B_2_a and $a.A_2_b_ID = AM.a_assocYA_B_2_b_ID )`) }) it('... managed associations with FKs being mix of struc and managed assoc', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1 as AM { ID } where exists a_strass`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze1 as AM { AM.ID } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze4 as a_strass where a_strass.A_1_a= AM.a_strass_A_1_a - and a_strass.A_1_b_assoc1_ID_1_a = AM.a_strass_A_1_b_assoc1_ID_1_a and a_strass.A_1_b_assoc1_ID_1_b = AM.a_strass_A_1_b_assoc1_ID_1_b - and a_strass.A_1_b_assoc1_ID_2_a = AM.a_strass_A_1_b_assoc1_ID_2_a and a_strass.A_1_b_assoc1_ID_2_b = AM.a_strass_A_1_b_assoc1_ID_2_b - and a_strass.A_1_b_assoc2_ID_1_a = AM.a_strass_A_1_b_assoc2_ID_1_a and a_strass.A_1_b_assoc2_ID_1_b = AM.a_strass_A_1_b_assoc2_ID_1_b - and a_strass.A_1_b_assoc2_ID_2_a = AM.a_strass_A_1_b_assoc2_ID_2_a and a_strass.A_1_b_assoc2_ID_2_b = AM.a_strass_A_1_b_assoc2_ID_2_b - and a_strass.A_2_a = AM.a_strass_A_2_a - and a_strass.A_2_b_assoc1_ID_1_a = AM.a_strass_A_2_b_assoc1_ID_1_a and a_strass.A_2_b_assoc1_ID_1_b = AM.a_strass_A_2_b_assoc1_ID_1_b - and a_strass.A_2_b_assoc1_ID_2_a = AM.a_strass_A_2_b_assoc1_ID_2_a and a_strass.A_2_b_assoc1_ID_2_b = AM.a_strass_A_2_b_assoc1_ID_2_b - and a_strass.A_2_b_assoc2_ID_1_a = AM.a_strass_A_2_b_assoc2_ID_1_a and a_strass.A_2_b_assoc2_ID_1_b = AM.a_strass_A_2_b_assoc2_ID_1_b - and a_strass.A_2_b_assoc2_ID_2_a = AM.a_strass_A_2_b_assoc2_ID_2_a and a_strass.A_2_b_assoc2_ID_2_b = AM.a_strass_A_2_b_assoc2_ID_2_b + SELECT 1 from bookshop.AssocMaze4 as $a where $a.A_1_a= AM.a_strass_A_1_a + and $a.A_1_b_assoc1_ID_1_a = AM.a_strass_A_1_b_assoc1_ID_1_a and $a.A_1_b_assoc1_ID_1_b = AM.a_strass_A_1_b_assoc1_ID_1_b + and $a.A_1_b_assoc1_ID_2_a = AM.a_strass_A_1_b_assoc1_ID_2_a and $a.A_1_b_assoc1_ID_2_b = AM.a_strass_A_1_b_assoc1_ID_2_b + and $a.A_1_b_assoc2_ID_1_a = AM.a_strass_A_1_b_assoc2_ID_1_a and $a.A_1_b_assoc2_ID_1_b = AM.a_strass_A_1_b_assoc2_ID_1_b + and $a.A_1_b_assoc2_ID_2_a = AM.a_strass_A_1_b_assoc2_ID_2_a and $a.A_1_b_assoc2_ID_2_b = AM.a_strass_A_1_b_assoc2_ID_2_b + and $a.A_2_a = AM.a_strass_A_2_a + and $a.A_2_b_assoc1_ID_1_a = AM.a_strass_A_2_b_assoc1_ID_1_a and $a.A_2_b_assoc1_ID_1_b = AM.a_strass_A_2_b_assoc1_ID_1_b + and $a.A_2_b_assoc1_ID_2_a = AM.a_strass_A_2_b_assoc1_ID_2_a and $a.A_2_b_assoc1_ID_2_b = AM.a_strass_A_2_b_assoc1_ID_2_b + and $a.A_2_b_assoc2_ID_1_a = AM.a_strass_A_2_b_assoc2_ID_1_a and $a.A_2_b_assoc2_ID_1_b = AM.a_strass_A_2_b_assoc2_ID_1_b + and $a.A_2_b_assoc2_ID_2_a = AM.a_strass_A_2_b_assoc2_ID_2_a and $a.A_2_b_assoc2_ID_2_b = AM.a_strass_A_2_b_assoc2_ID_2_b )`) }) @@ -689,7 +689,7 @@ describe('EXISTS predicate in where', () => { it('... managed association with explicit FKs being path into a struc', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1 as AM { ID } where exists a_part`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze1 as AM { AM.ID } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze2 as a_part where a_part.A_1_a = AM.a_part_a and a_part.S_2_b = AM.a_part_b + SELECT 1 from bookshop.AssocMaze2 as $a where $a.A_1_a = AM.a_part_a and $a.S_2_b = AM.a_part_b )`) }) }) @@ -704,27 +704,27 @@ describe('EXISTS predicate in infix filter', () => { it('... in select', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books {ID, genre[exists children].descr }`, model) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books - LEFT OUTER JOIN bookshop.Genres as genre ON genre.ID = Books.genre_ID + cds.ql`SELECT from bookshop.Books as $B + LEFT OUTER JOIN bookshop.Genres as genre ON genre.ID = $B.genre_ID and EXISTS ( - SELECT 1 from bookshop.Genres as children where children.parent_ID = genre.ID + SELECT 1 from bookshop.Genres as $c where $c.parent_ID = genre.ID ) - { Books.ID, genre.descr as genre_descr }`, + { $B.ID, genre.descr as genre_descr }`, ) }) it('... in select, nested', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books {ID, genre[exists children[exists children]].descr }`, model) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books - LEFT OUTER JOIN bookshop.Genres as genre ON genre.ID = Books.genre_ID + cds.ql`SELECT from bookshop.Books as $B + LEFT OUTER JOIN bookshop.Genres as genre ON genre.ID = $B.genre_ID and EXISTS ( - SELECT 1 from bookshop.Genres as children where children.parent_ID = genre.ID + SELECT 1 from bookshop.Genres as $c where $c.parent_ID = genre.ID and EXISTS ( - SELECT 1 from bookshop.Genres as children2 where children2.parent_ID = children.ID + SELECT 1 from bookshop.Genres as $c2 where $c2.parent_ID = $c.ID ) ) - { Books.ID, genre.descr as genre_descr }`, + { $B.ID, genre.descr as genre_descr }`, ) }) @@ -734,18 +734,18 @@ describe('EXISTS predicate in infix filter', () => { model, ) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Books as Books - LEFT OUTER JOIN bookshop.Genres as genre ON genre.ID = Books.genre_ID + cds.ql`SELECT from bookshop.Books as $B + LEFT OUTER JOIN bookshop.Genres as genre ON genre.ID = $B.genre_ID and EXISTS ( - SELECT 1 from bookshop.Genres as children2 where children2.parent_ID = genre.ID - and children2.code = 2 + SELECT 1 from bookshop.Genres as $c where $c.parent_ID = genre.ID + and $c.code = 2 ) LEFT OUTER JOIN bookshop.Genres as children ON children.parent_ID = genre.ID and EXISTS ( - SELECT 1 from bookshop.Genres as children3 where children3.parent_ID = children.ID - and children3.code = 3 + SELECT 1 from bookshop.Genres as $c2 where $c2.parent_ID = children.ID + and $c2.code = 3 ) - { Books.ID, children.descr as genre_children_descr }`, + { $B.ID, children.descr as genre_children_descr }`, ) }) it('reject non foreign key access in infix filter', async () => { @@ -795,16 +795,16 @@ describe('Scoped queries', () => { delete originalQuery.SELECT.expand expect(query).to.deep.equal(cds.ql` - SELECT from bookshop.Genres as parent { parent.ID } + SELECT from bookshop.Genres as $p { $p.ID } where exists ( - SELECT 1 from bookshop.Genres as parent2 - where parent2.parent_ID = parent.ID and + SELECT 1 from bookshop.Genres as $p2 + where $p2.parent_ID = $p.ID and exists ( - SELECT 1 from bookshop.Genres as parent3 - where parent3.parent_ID = parent2.ID and + SELECT 1 from bookshop.Genres as $p3 + where $p3.parent_ID = $p2.ID and exists ( - SELECT 1 from bookshop.Genres as Genres - where Genres.parent_ID = parent3.ID + SELECT 1 from bookshop.Genres as $G + where $G.parent_ID = $p3.ID ) ) ) @@ -816,7 +816,7 @@ describe('Scoped queries', () => { //(SMW) TODO I'd prefer to have the cond from the filter before the cond coming from the WHERE // which, by the way, is the case in tests below where we have a path in FROM -> ??? it('handles infix filter at entity and WHERE clause', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books[price < 12.13]{Books.ID} where stock < 11`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books[price < 12.13] as Books {Books.ID} where stock < 11`, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Books as Books {Books.ID} WHERE (Books.stock < 11) and (Books.price < 12.13)`, ) @@ -824,8 +824,8 @@ describe('Scoped queries', () => { it('handles multiple assoc steps', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.TestPublisher:texts {ID}`, model) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.TestPublisher.texts as texts {texts.ID} WHERE exists ( - SELECT 1 from bookshop.TestPublisher as TestPublisher where texts.publisher_structuredKey_ID = TestPublisher.publisher_structuredKey_ID + cds.ql`SELECT from bookshop.TestPublisher.texts as $t {$t.ID} WHERE exists ( + SELECT 1 from bookshop.TestPublisher as $T2 where $t.publisher_structuredKey_ID = $T2.publisher_structuredKey_ID )`, ) }) @@ -841,7 +841,7 @@ describe('Scoped queries', () => { it('handles infix filter with nested xpr at entity and WHERE clause', () => { let query = cqn4sql( cds.ql` - SELECT from bookshop.Books[not (price < 12.13)] { Books.ID } where stock < 11 + SELECT from bookshop.Books[not (price < 12.13)] as Books { Books.ID } where stock < 11 `, model, ) @@ -854,7 +854,7 @@ describe('Scoped queries', () => { // which, by the way, is the case in tests below where we have a path in FROM -> ??? it('gets precedence right for infix filter at entity and WHERE clause', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books[price < 12.13 or stock > 77] {Books.ID} where stock < 11 or price > 17.89`, + cds.ql`SELECT from bookshop.Books[price < 12.13 or stock > 77] as Books {Books.ID} where stock < 11 or price > 17.89`, model, ) expect(query).to.deep.equal( @@ -865,93 +865,93 @@ describe('Scoped queries', () => { it('FROM path ends on to-one association', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books:author { name }`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as author { author.name } - WHERE EXISTS ( SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $a { $a.name } + WHERE EXISTS ( SELECT 1 from bookshop.Books as $B where $B.author_ID = $a.ID )`) }) it('unmanaged to one with (multiple) $self in on-condition', () => { // $self in refs of length > 1 can just be ignored semantically let query = cqn4sql(cds.ql`SELECT from bookshop.Books:coAuthorUnmanaged { name }`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as coAuthorUnmanaged { coAuthorUnmanaged.name } - WHERE EXISTS ( SELECT 1 from bookshop.Books as Books where coAuthorUnmanaged.ID = Books.coAuthor_ID_unmanaged + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $c { $c.name } + WHERE EXISTS ( SELECT 1 from bookshop.Books as $B where $c.ID = $B.coAuthor_ID_unmanaged )`) }) it('handles FROM path with association with explicit table alias', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books:author as author { author.name }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as author { author.name } - WHERE EXISTS ( SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID + WHERE EXISTS ( SELECT 1 from bookshop.Books as $B where $B.author_ID = author.ID )`) }) it('handles FROM path with association with mean explicit table alias', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books:author as Books { name, Books.dateOfBirth }`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as Books { Books.name, Books.dateOfBirth} - WHERE EXISTS ( SELECT 1 from bookshop.Books as Books2 where Books2.author_ID = Books.ID + let query = cqn4sql(cds.ql`SELECT from bookshop.Books:author as $B { name, $B.dateOfBirth }`, model) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $B { $B.name, $B.dateOfBirth} + WHERE EXISTS ( SELECT 1 from bookshop.Books as $B2 where $B2.author_ID = $B.ID )`) }) it('handles FROM path with backlink association', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:books {books.ID}`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:books as books {books.ID}`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as books {books.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Authors as Authors where Authors.ID = books.author_ID + SELECT 1 from bookshop.Authors as $A where $A.ID = books.author_ID )`) }) it('handles FROM path with backlink association for association-like calculated element', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:booksWithALotInStock {booksWithALotInStock.ID}`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:booksWithALotInStock as booksWithALotInStock {booksWithALotInStock.ID}`, model) expect(query).to.deep .equal(cds.ql`SELECT from bookshop.Books as booksWithALotInStock {booksWithALotInStock.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Authors as Authors where ( Authors.ID = booksWithALotInStock.author_ID ) and ( booksWithALotInStock.stock > 100 ) + SELECT 1 from bookshop.Authors as $A where ( $A.ID = booksWithALotInStock.author_ID ) and ( booksWithALotInStock.stock > 100 ) )`) }) it('handles FROM path with unmanaged composition and prepends source side alias', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books:texts { locale }`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books.texts as texts {texts.locale} WHERE EXISTS ( - SELECT 1 from bookshop.Books as Books where texts.ID = Books.ID + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books.texts as $t {$t.locale} WHERE EXISTS ( + SELECT 1 from bookshop.Books as $B where $t.ID = $B.ID )`) }) it('handles FROM path with struct and association', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books:dedication.addressee { dateOfBirth }`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Person as addressee { addressee.dateOfBirth } - WHERE EXISTS ( SELECT 1 from bookshop.Books as Books where Books.dedication_addressee_ID = addressee.ID + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Person as $a { $a.dateOfBirth } + WHERE EXISTS ( SELECT 1 from bookshop.Books as $B where $B.dedication_addressee_ID = $a.ID )`) }) it('handles FROM path with struct and association (2)', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.DeepRecursiveAssoc:one.two.three.toSelf { ID }`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.DeepRecursiveAssoc as toSelf { toSelf.ID } + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.DeepRecursiveAssoc as $t { $t.ID } WHERE EXISTS ( - SELECT 1 from bookshop.DeepRecursiveAssoc as DeepRecursiveAssoc where DeepRecursiveAssoc.one_two_three_toSelf_ID = toSelf.ID + SELECT 1 from bookshop.DeepRecursiveAssoc as $D where $D.one_two_three_toSelf_ID = $t.ID )`) }) it('handles FROM path with filter at entity plus association', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books[ID=201]:author {author.ID}`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books[ID=201]:author as author {author.ID}`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as author {author.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID and Books.ID=201 + SELECT 1 from bookshop.Books as $B where $B.author_ID = author.ID and $B.ID=201 )`) }) // (SMW) here the explicit WHERE comes at the end (as it should be) it('handles FROM path with association and filters and WHERE', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books[ID=201 or ID=202]:author[ID=4711 or ID=4712]{author.ID} where author.name='foo' or name='bar'`, + cds.ql`SELECT from bookshop.Books[ID=201 or ID=202]:author[ID=4711 or ID=4712] as author {author.ID} where author.name='foo' or name='bar'`, model, ) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Authors as author {author.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID and (Books.ID=201 or Books.ID=202) + SELECT 1 from bookshop.Books as $B where $B.author_ID = author.ID and ($B.ID=201 or $B.ID=202) ) and (author.ID=4711 or author.ID=4712) and (author.name='foo' or author.name='bar')`, ) }) it('handles FROM path with association with one infix filter at leaf step', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books:author[ID=4711] {author.ID}`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books:author[ID=4711] as author {author.ID}`, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Authors as author {author.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID + SELECT 1 from bookshop.Books as $B where $B.author_ID = author.ID ) and author.ID=4711`, ) }) @@ -965,9 +965,9 @@ describe('Scoped queries', () => { // (PB) modified -> additional where condition e.g. infix filter in result are wrapped in `xpr` it('MUST ... in from clauses with infix filters, ODATA variant w/o mentioning key', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books[201]:author[150] {ID}`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as author {author.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID and Books.ID=201 - ) AND author.ID = 150`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Authors as $a {$a.ID} WHERE EXISTS ( + SELECT 1 from bookshop.Books as $B where $B.author_ID = $a.ID and $B.ID=201 + ) AND $a.ID = 150`) }) // (SMW) TODO msg not good -> filter in general is ok for assoc with multiple FKS, @@ -982,9 +982,9 @@ describe('Scoped queries', () => { // (SMW) TODO: check it('MUST ... in from clauses with infix filters ODATA variant w/o mentioning key ORDERS/ITEMS', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Orders[201]:items[2] {pos}`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Orders.items as items {items.pos} WHERE EXISTS ( - SELECT 1 from bookshop.Orders as Orders where Orders.ID = items.up__ID and Orders.ID = 201 - ) AND items.pos = 2`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Orders.items as $i {$i.pos} WHERE EXISTS ( + SELECT 1 from bookshop.Orders as $O where $O.ID = $i.up__ID and $O.ID = 201 + ) AND $i.pos = 2`) }) // usually, "Filters can only be applied to managed associations which result in a single foreign key" @@ -992,16 +992,16 @@ describe('Scoped queries', () => { // `where` condition of the exists subquery. Hence we enable this shortcut notation. it('MUST ... contain foreign keys of backlink association in on-condition?', () => { const query = cqn4sql(cds.ql`SELECT from bookshop.Orders:items[2] {pos}`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Orders.items as items {items.pos} WHERE EXISTS ( - SELECT 1 from bookshop.Orders as Orders where Orders.ID = items.up__ID - ) and items.pos = 2`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Orders.items as $i {$i.pos} WHERE EXISTS ( + SELECT 1 from bookshop.Orders as $O where $O.ID = $i.up__ID + ) and $i.pos = 2`) }) it('same as above but mention key', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Orders:items[pos=2] {pos}`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Orders.items as items {items.pos} WHERE EXISTS ( - SELECT 1 from bookshop.Orders as Orders where Orders.ID = items.up__ID - ) and items.pos = 2`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Orders.items as $i {$i.pos} WHERE EXISTS ( + SELECT 1 from bookshop.Orders as $O where $O.ID = $i.up__ID + ) and $i.pos = 2`) }) // TODO @@ -1013,22 +1013,22 @@ describe('Scoped queries', () => { it('MUST ... be possible to address fully qualified, partial key in infix filter', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Orders.items[pos=2] {pos}`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Orders.items as items {items.pos} where items.pos = 2`) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Orders.items as $i {$i.pos} where $i.pos = 2`) }) it('handles paths with two associations', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:books.genre {genre.ID}`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:books.genre as genre {genre.ID}`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Genres as genre {genre.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.genre_ID = genre.ID and EXISTS ( - SELECT 1 from bookshop.Authors as Authors where Authors.ID = books.author_ID + SELECT 1 from bookshop.Books as $b where $b.genre_ID = genre.ID and EXISTS ( + SELECT 1 from bookshop.Authors as $A where $A.ID = $b.author_ID ) )`) }) it('handles paths with two associations, first is association-like calculated element', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:booksWithALotInStock.genre {genre.ID}`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:booksWithALotInStock.genre as genre {genre.ID}`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Genres as genre {genre.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as booksWithALotInStock where booksWithALotInStock.genre_ID = genre.ID and EXISTS ( - SELECT 1 from bookshop.Authors as Authors where ( Authors.ID = booksWithALotInStock.author_ID ) and ( booksWithALotInStock.stock > 100 ) + SELECT 1 from bookshop.Books as $b where $b.genre_ID = genre.ID and EXISTS ( + SELECT 1 from bookshop.Authors as $A where ( $A.ID = $b.author_ID ) and ( $b.stock > 100 ) ) )`) }) @@ -1036,32 +1036,32 @@ describe('Scoped queries', () => { it('handles paths with two associations (mean alias)', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:books.genre as books {books.ID}`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Genres as books {books.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.genre_ID = books.ID and EXISTS ( - SELECT 1 from bookshop.Authors as Authors where Authors.ID = books2.author_ID + SELECT 1 from bookshop.Books as $b where $b.genre_ID = books.ID and EXISTS ( + SELECT 1 from bookshop.Authors as $A where $A.ID = $b.author_ID ) )`) }) it('handles paths with three associations', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:books.genre.parent {parent.ID}`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Genres as parent {parent.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Genres as genre where genre.parent_ID = parent.ID and EXISTS ( - SELECT 1 from bookshop.Books as books where books.genre_ID = genre.ID and EXISTS ( - SELECT 1 from bookshop.Authors as Authors where Authors.ID = books.author_ID + let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:books.genre.parent as $p {$p.ID}`, model) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Genres as $p {$p.ID} WHERE EXISTS ( + SELECT 1 from bookshop.Genres as $g where $g.parent_ID = $p.ID and EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.genre_ID = $g.ID and EXISTS ( + SELECT 1 from bookshop.Authors as $A where $A.ID = $b.author_ID ) ) )`) }) it('handles paths with recursive associations', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:books.genre.parent.parent.parent {parent.ID}`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Genres as parent {parent.ID} + let query = cqn4sql(cds.ql`SELECT from bookshop.Authors:books.genre.parent.parent.parent as $p {$p.ID}`, model) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Genres as $p {$p.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Genres as parent2 where parent2.parent_ID = parent.ID and EXISTS ( - SELECT 1 from bookshop.Genres as parent3 where parent3.parent_ID = parent2.ID and EXISTS ( - SELECT 1 from bookshop.Genres as genre where genre.parent_ID = parent3.ID and EXISTS ( - SELECT 1 from bookshop.Books as books where books.genre_ID = genre.ID and EXISTS ( - SELECT 1 from bookshop.Authors as Authors where Authors.ID = books.author_ID + SELECT 1 from bookshop.Genres as $p2 where $p2.parent_ID = $p.ID and EXISTS ( + SELECT 1 from bookshop.Genres as $p3 where $p3.parent_ID = $p2.ID and EXISTS ( + SELECT 1 from bookshop.Genres as $g where $g.parent_ID = $p3.ID and EXISTS ( + SELECT 1 from bookshop.Books as $b where $b.genre_ID = $g.ID and EXISTS ( + SELECT 1 from bookshop.Authors as $A where $A.ID = $b.author_ID ) ) ) @@ -1071,31 +1071,31 @@ describe('Scoped queries', () => { it('handles paths with unmanaged association', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Baz:parent {id}`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Baz as parent {parent.id} WHERE EXISTS ( - SELECT 1 from bookshop.Baz as Baz where parent.id = Baz.parent_id or parent.id > 17 + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Baz as $p {$p.id} WHERE EXISTS ( + SELECT 1 from bookshop.Baz as $B where $p.id = $B.parent_id or $p.id > 17 )`) }) it('handles paths with unmanaged association with alias', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Baz:parent as A {id}`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Baz as A {A.id} WHERE EXISTS ( - SELECT 1 from bookshop.Baz as Baz where A.id = Baz.parent_id or A.id > 17 + SELECT 1 from bookshop.Baz as $B where A.id = $B.parent_id or A.id > 17 )`) }) // (SMW) need more tests with unmanaged ON conds using all sorts of stuff -> e.g. struc access in ON, FK of mgd assoc in FROM ... it('transforms unmanaged association to where exists subquery and infix filter', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Baz:parent[id<20] {parent.id}`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Baz as parent {parent.id} WHERE EXISTS ( - SELECT 1 from bookshop.Baz as Baz where parent.id = Baz.parent_id or parent.id > 17 - ) AND parent.id < 20`) + let query = cqn4sql(cds.ql`SELECT from bookshop.Baz:parent[id<20] as my {my.id}`, model) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Baz as my {my.id} WHERE EXISTS ( + SELECT 1 from bookshop.Baz as $B where my.id = $B.parent_id or my.id > 17 + ) AND my.id < 20`) }) it('transforms unmanaged association to where exists subquery with multiple infix filter', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Baz:parent[id<20 or id > 12] {parent.id}`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Baz as parent {parent.id} WHERE EXISTS ( - SELECT 1 from bookshop.Baz as Baz where parent.id = Baz.parent_id or parent.id > 17 - ) AND (parent.id < 20 or parent.id > 12)`) + let query = cqn4sql(cds.ql`SELECT from bookshop.Baz:parent[id<20 or id > 12] as my { my.id }`, model) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Baz as my {my.id} WHERE EXISTS ( + SELECT 1 from bookshop.Baz as $B where my.id = $B.parent_id or my.id > 17 + ) AND (my.id < 20 or my.id > 12)`) }) // @@ -1105,9 +1105,9 @@ describe('Scoped queries', () => { it('exists predicate in infix filter in FROM', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Authors[exists books] {ID}`, model) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Authors as Authors {Authors.ID} + cds.ql`SELECT from bookshop.Authors as $A {$A.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as books where books.author_ID = Authors.ID + SELECT 1 from bookshop.Books as $b where $b.author_ID = $A.ID )`, ) }) @@ -1115,11 +1115,11 @@ describe('Scoped queries', () => { it('exists predicate in infix filter at ssoc path step in FROM', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books:author[exists books] {ID}`, model) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Authors as author {author.ID} + cds.ql`SELECT from bookshop.Authors as $a {$a.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID + SELECT 1 from bookshop.Books as $B where $B.author_ID = $a.ID ) and EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = author.ID + SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a.ID )`, ) }) @@ -1130,16 +1130,16 @@ describe('Scoped queries', () => { model, ) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Authors as author {author.ID} + cds.ql`SELECT from bookshop.Authors as $a {$a.ID} where exists ( - SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID + SELECT 1 from bookshop.Books as $B where $B.author_ID = $a.ID ) and exists ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = author.ID + SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a.ID and ( exists ( - SELECT 1 from bookshop.Authors as coAuthorUnmanaged where coAuthorUnmanaged.ID = books2.coAuthor_ID_unmanaged - ) or books2.title = 'Sturmhöhe' + SELECT 1 from bookshop.Authors as $c where $c.ID = $b2.coAuthor_ID_unmanaged + ) or $b2.title = 'Sturmhöhe' ) ) `, @@ -1149,11 +1149,11 @@ describe('Scoped queries', () => { it('exists predicate in infix filter followed by assoc in FROM', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books[exists genre]:author {ID}`, model) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Authors as author {author.ID} + cds.ql`SELECT from bookshop.Authors as $a {$a.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID + SELECT 1 from bookshop.Books as $B where $B.author_ID = $a.ID and EXISTS ( - SELECT 1 from bookshop.Genres as genre where genre.ID = Books.genre_ID + SELECT 1 from bookshop.Genres as $g where $g.ID = $B.genre_ID ) )`, ) @@ -1162,14 +1162,14 @@ describe('Scoped queries', () => { it('exists predicate in infix filters in FROM', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books[exists genre]:author[exists books] {ID}`, model) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Authors as author {author.ID} + cds.ql`SELECT from bookshop.Authors as $a {$a.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Books as Books where Books.author_ID = author.ID + SELECT 1 from bookshop.Books as $B where $B.author_ID = $a.ID and EXISTS ( - SELECT 1 from bookshop.Genres as genre where genre.ID = Books.genre_ID + SELECT 1 from bookshop.Genres as $g where $g.ID = $B.genre_ID ) ) and EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = author.ID + SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a.ID )`, ) }) @@ -1181,95 +1181,95 @@ describe('Scoped queries', () => { model, ) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Books as books {books.ID} + cds.ql`SELECT from bookshop.Books as $b {$b.ID} WHERE EXISTS ( - SELECT 1 from bookshop.Authors as author where author.ID = books.author_ID + SELECT 1 from bookshop.Authors as $a where $a.ID = $b.author_ID and EXISTS ( - SELECT 1 from bookshop.Books as books2 where books2.author_ID = author.ID + SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a.ID ) and EXISTS ( - SELECT 1 from bookshop.Books as Books3 where Books3.author_ID = author.ID + SELECT 1 from bookshop.Books as $B3 where $B3.author_ID = $a.ID and EXISTS ( - SELECT 1 from bookshop.Genres as genre where genre.ID = Books3.genre_ID + SELECT 1 from bookshop.Genres as $g where $g.ID = $B3.genre_ID ) ) ) and EXISTS ( - SELECT 1 from bookshop.Genres as genre2 where genre2.ID = books.genre_ID + SELECT 1 from bookshop.Genres as $g2 where $g2.ID = $b.genre_ID )`, ) }) it('... managed association with structured FK', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_struc { val }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_struc as a_struc { val }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze2 as a_struc { a_struc.val } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze1 as AssocMaze1 where AssocMaze1.a_struc_ID_1_a = a_struc.ID_1_a and AssocMaze1.a_struc_ID_1_b = a_struc.ID_1_b - and AssocMaze1.a_struc_ID_2_a = a_struc.ID_2_a and AssocMaze1.a_struc_ID_2_b = a_struc.ID_2_b + SELECT 1 from bookshop.AssocMaze1 as $A where $A.a_struc_ID_1_a = a_struc.ID_1_a and $A.a_struc_ID_1_b = a_struc.ID_1_b + and $A.a_struc_ID_2_a = a_struc.ID_2_a and $A.a_struc_ID_2_b = a_struc.ID_2_b )`) }) it('... managed association with explicit simple FKs', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_strucX { val }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_strucX as a_strucX { val }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze2 as a_strucX { a_strucX.val } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze1 as AssocMaze1 where AssocMaze1.a_strucX_a = a_strucX.a and AssocMaze1.a_strucX_b = a_strucX.b + SELECT 1 from bookshop.AssocMaze1 as $A where $A.a_strucX_a = a_strucX.a and $A.a_strucX_b = a_strucX.b )`) }) it('... managed association with explicit structured FKs', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_strucY { val }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_strucY as a_strucY { val }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze2 as a_strucY { a_strucY.val } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze1 as AssocMaze1 where AssocMaze1.a_strucY_S_1_a = a_strucY.S_1_a and AssocMaze1.a_strucY_S_1_b = a_strucY.S_1_b - and AssocMaze1.a_strucY_S_2_a = a_strucY.S_2_a and AssocMaze1.a_strucY_S_2_b = a_strucY.S_2_b + SELECT 1 from bookshop.AssocMaze1 as $A where $A.a_strucY_S_1_a = a_strucY.S_1_a and $A.a_strucY_S_1_b = a_strucY.S_1_b + and $A.a_strucY_S_2_a = a_strucY.S_2_a and $A.a_strucY_S_2_b = a_strucY.S_2_b )`) }) it('... managed association with explicit structured aliased FKs', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_strucXA { val }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_strucXA as a_strucXA { val }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze2 as a_strucXA { a_strucXA.val } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze1 as AssocMaze1 where AssocMaze1.a_strucXA_T_1_a = a_strucXA.S_1_a and AssocMaze1.a_strucXA_T_1_b = a_strucXA.S_1_b - and AssocMaze1.a_strucXA_T_2_a = a_strucXA.S_2_a and AssocMaze1.a_strucXA_T_2_b = a_strucXA.S_2_b + SELECT 1 from bookshop.AssocMaze1 as $A where $A.a_strucXA_T_1_a = a_strucXA.S_1_a and $A.a_strucXA_T_1_b = a_strucXA.S_1_b + and $A.a_strucXA_T_2_a = a_strucXA.S_2_a and $A.a_strucXA_T_2_b = a_strucXA.S_2_b )`) }) it('... managed associations with FKs being managed associations', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_assoc { val }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_assoc as a_assoc { val }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze3 as a_assoc { a_assoc.val } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze1 as AssocMaze1 where AssocMaze1.a_assoc_assoc1_ID_1_a = a_assoc.assoc1_ID_1_a and AssocMaze1.a_assoc_assoc1_ID_1_b = a_assoc.assoc1_ID_1_b - and AssocMaze1.a_assoc_assoc1_ID_2_a = a_assoc.assoc1_ID_2_a and AssocMaze1.a_assoc_assoc1_ID_2_b = a_assoc.assoc1_ID_2_b - and AssocMaze1.a_assoc_assoc2_ID_1_a = a_assoc.assoc2_ID_1_a and AssocMaze1.a_assoc_assoc2_ID_1_b = a_assoc.assoc2_ID_1_b - and AssocMaze1.a_assoc_assoc2_ID_2_a = a_assoc.assoc2_ID_2_a and AssocMaze1.a_assoc_assoc2_ID_2_b = a_assoc.assoc2_ID_2_b + SELECT 1 from bookshop.AssocMaze1 as $A where $A.a_assoc_assoc1_ID_1_a = a_assoc.assoc1_ID_1_a and $A.a_assoc_assoc1_ID_1_b = a_assoc.assoc1_ID_1_b + and $A.a_assoc_assoc1_ID_2_a = a_assoc.assoc1_ID_2_a and $A.a_assoc_assoc1_ID_2_b = a_assoc.assoc1_ID_2_b + and $A.a_assoc_assoc2_ID_1_a = a_assoc.assoc2_ID_1_a and $A.a_assoc_assoc2_ID_1_b = a_assoc.assoc2_ID_1_b + and $A.a_assoc_assoc2_ID_2_a = a_assoc.assoc2_ID_2_a and $A.a_assoc_assoc2_ID_2_b = a_assoc.assoc2_ID_2_b )`) }) it('... managed association with explicit FKs being managed associations', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_assocY { val }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_assocY as a_assocY { val }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze2 as a_assocY { a_assocY.val } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze1 as AssocMaze1 where AssocMaze1.a_assocY_A_1_a = a_assocY.A_1_a and AssocMaze1.a_assocY_A_1_b_ID = a_assocY.A_1_b_ID - and AssocMaze1.a_assocY_A_2_a = a_assocY.A_2_a and AssocMaze1.a_assocY_A_2_b_ID = a_assocY.A_2_b_ID + SELECT 1 from bookshop.AssocMaze1 as $A where $A.a_assocY_A_1_a = a_assocY.A_1_a and $A.a_assocY_A_1_b_ID = a_assocY.A_1_b_ID + and $A.a_assocY_A_2_a = a_assocY.A_2_a and $A.a_assocY_A_2_b_ID = a_assocY.A_2_b_ID )`) }) it('... managed association with explicit aliased FKs being managed associations', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_assocYA { val }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_assocYA as a_assocYA { val }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze2 as a_assocYA { a_assocYA.val } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze1 as AssocMaze1 where AssocMaze1.a_assocYA_B_1_a = a_assocYA.A_1_a and AssocMaze1.a_assocYA_B_1_b_ID = a_assocYA.A_1_b_ID - and AssocMaze1.a_assocYA_B_2_a = a_assocYA.A_2_a and AssocMaze1.a_assocYA_B_2_b_ID = a_assocYA.A_2_b_ID + SELECT 1 from bookshop.AssocMaze1 as $A where $A.a_assocYA_B_1_a = a_assocYA.A_1_a and $A.a_assocYA_B_1_b_ID = a_assocYA.A_1_b_ID + and $A.a_assocYA_B_2_a = a_assocYA.A_2_a and $A.a_assocYA_B_2_b_ID = a_assocYA.A_2_b_ID )`) }) it('... managed associations with FKs being mix of struc and managed assoc', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_strass { val }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.AssocMaze1:a_strass as a_strass { val }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.AssocMaze4 as a_strass { a_strass.val } WHERE EXISTS ( - SELECT 1 from bookshop.AssocMaze1 as AssocMaze1 - where AssocMaze1.a_strass_A_1_a = a_strass.A_1_a - and AssocMaze1.a_strass_A_1_b_assoc1_ID_1_a = a_strass.A_1_b_assoc1_ID_1_a and AssocMaze1.a_strass_A_1_b_assoc1_ID_1_b = a_strass.A_1_b_assoc1_ID_1_b - and AssocMaze1.a_strass_A_1_b_assoc1_ID_2_a = a_strass.A_1_b_assoc1_ID_2_a and AssocMaze1.a_strass_A_1_b_assoc1_ID_2_b = a_strass.A_1_b_assoc1_ID_2_b - and AssocMaze1.a_strass_A_1_b_assoc2_ID_1_a = a_strass.A_1_b_assoc2_ID_1_a and AssocMaze1.a_strass_A_1_b_assoc2_ID_1_b = a_strass.A_1_b_assoc2_ID_1_b - and AssocMaze1.a_strass_A_1_b_assoc2_ID_2_a = a_strass.A_1_b_assoc2_ID_2_a and AssocMaze1.a_strass_A_1_b_assoc2_ID_2_b = a_strass.A_1_b_assoc2_ID_2_b - and AssocMaze1.a_strass_A_2_a = a_strass.A_2_a - and AssocMaze1.a_strass_A_2_b_assoc1_ID_1_a = a_strass.A_2_b_assoc1_ID_1_a and AssocMaze1.a_strass_A_2_b_assoc1_ID_1_b = a_strass.A_2_b_assoc1_ID_1_b - and AssocMaze1.a_strass_A_2_b_assoc1_ID_2_a = a_strass.A_2_b_assoc1_ID_2_a and AssocMaze1.a_strass_A_2_b_assoc1_ID_2_b = a_strass.A_2_b_assoc1_ID_2_b - and AssocMaze1.a_strass_A_2_b_assoc2_ID_1_a = a_strass.A_2_b_assoc2_ID_1_a and AssocMaze1.a_strass_A_2_b_assoc2_ID_1_b = a_strass.A_2_b_assoc2_ID_1_b - and AssocMaze1.a_strass_A_2_b_assoc2_ID_2_a = a_strass.A_2_b_assoc2_ID_2_a and AssocMaze1.a_strass_A_2_b_assoc2_ID_2_b = a_strass.A_2_b_assoc2_ID_2_b + SELECT 1 from bookshop.AssocMaze1 as $A + where $A.a_strass_A_1_a = a_strass.A_1_a + and $A.a_strass_A_1_b_assoc1_ID_1_a = a_strass.A_1_b_assoc1_ID_1_a and $A.a_strass_A_1_b_assoc1_ID_1_b = a_strass.A_1_b_assoc1_ID_1_b + and $A.a_strass_A_1_b_assoc1_ID_2_a = a_strass.A_1_b_assoc1_ID_2_a and $A.a_strass_A_1_b_assoc1_ID_2_b = a_strass.A_1_b_assoc1_ID_2_b + and $A.a_strass_A_1_b_assoc2_ID_1_a = a_strass.A_1_b_assoc2_ID_1_a and $A.a_strass_A_1_b_assoc2_ID_1_b = a_strass.A_1_b_assoc2_ID_1_b + and $A.a_strass_A_1_b_assoc2_ID_2_a = a_strass.A_1_b_assoc2_ID_2_a and $A.a_strass_A_1_b_assoc2_ID_2_b = a_strass.A_1_b_assoc2_ID_2_b + and $A.a_strass_A_2_a = a_strass.A_2_a + and $A.a_strass_A_2_b_assoc1_ID_1_a = a_strass.A_2_b_assoc1_ID_1_a and $A.a_strass_A_2_b_assoc1_ID_1_b = a_strass.A_2_b_assoc1_ID_1_b + and $A.a_strass_A_2_b_assoc1_ID_2_a = a_strass.A_2_b_assoc1_ID_2_a and $A.a_strass_A_2_b_assoc1_ID_2_b = a_strass.A_2_b_assoc1_ID_2_b + and $A.a_strass_A_2_b_assoc2_ID_1_a = a_strass.A_2_b_assoc2_ID_1_a and $A.a_strass_A_2_b_assoc2_ID_1_b = a_strass.A_2_b_assoc2_ID_1_b + and $A.a_strass_A_2_b_assoc2_ID_2_a = a_strass.A_2_b_assoc2_ID_2_a and $A.a_strass_A_2_b_assoc2_ID_2_b = a_strass.A_2_b_assoc2_ID_2_b )`) }) @@ -1278,22 +1278,22 @@ describe('Scoped queries', () => { SELECT from bookshop.WorklistItems[ID = 1 and snapshotHash = 0]:releaseChecks[ID = 1 and snapshotHash = 0].detailsDeviations ` const expected = cds.ql` - SELECT from bookshop.QualityDeviations as detailsDeviations { - detailsDeviations.snapshotHash, - detailsDeviations.ID, - detailsDeviations.batch_ID, - detailsDeviations.material_ID, + SELECT from bookshop.QualityDeviations as $d { + $d.snapshotHash, + $d.ID, + $d.batch_ID, + $d.material_ID, } where exists ( - SELECT 1 from bookshop.WorklistItem_ReleaseChecks as releaseChecks - where detailsDeviations.material_ID = releaseChecks.parent_releaseDecisionTrigger_batch_material_ID - and ( detailsDeviations.batch_ID = '*' or detailsDeviations.batch_ID = releaseChecks.parent_releaseDecisionTrigger_batch_ID ) - and detailsDeviations.snapshotHash = releaseChecks.snapshotHash - and releaseChecks.ID = 1 and releaseChecks.snapshotHash = 0 + SELECT 1 from bookshop.WorklistItem_ReleaseChecks as $r + where $d.material_ID = $r.parent_releaseDecisionTrigger_batch_material_ID + and ( $d.batch_ID = '*' or $d.batch_ID = $r.parent_releaseDecisionTrigger_batch_ID ) + and $d.snapshotHash = $r.snapshotHash + and $r.ID = 1 and $r.snapshotHash = 0 and exists ( - SELECT 1 from bookshop.WorklistItems as WorklistItems - where releaseChecks.parent_ID = WorklistItems.ID - and releaseChecks.parent_snapshotHash = WorklistItems.snapshotHash - and WorklistItems.ID = 1 and WorklistItems.snapshotHash = 0 + SELECT 1 from bookshop.WorklistItems as $W + where $r.parent_ID = $W.ID + and $r.parent_snapshotHash = $W.snapshotHash + and $W.ID = 1 and $W.snapshotHash = 0 ) ) ` @@ -1306,29 +1306,29 @@ describe('Scoped queries', () => { .detailsDeviations[ID='0' and snapshotHash='0'and batch_ID='*' and material_ID='1'] ` const expected = cds.ql` - SELECT from bookshop.QualityDeviations as detailsDeviations { - detailsDeviations.snapshotHash, - detailsDeviations.ID, - detailsDeviations.batch_ID, - detailsDeviations.material_ID, + SELECT from bookshop.QualityDeviations as $d { + $d.snapshotHash, + $d.ID, + $d.batch_ID, + $d.material_ID, } where exists ( - SELECT 1 from bookshop.WorklistItem_ReleaseChecks as releaseChecks - where detailsDeviations.material_ID = releaseChecks.parent_releaseDecisionTrigger_batch_material_ID - and ( detailsDeviations.batch_ID = '*' or detailsDeviations.batch_ID = releaseChecks.parent_releaseDecisionTrigger_batch_ID ) - and detailsDeviations.snapshotHash = releaseChecks.snapshotHash - and releaseChecks.ID = 1 and releaseChecks.snapshotHash = 0 + SELECT 1 from bookshop.WorklistItem_ReleaseChecks as $r + where $d.material_ID = $r.parent_releaseDecisionTrigger_batch_material_ID + and ( $d.batch_ID = '*' or $d.batch_ID = $r.parent_releaseDecisionTrigger_batch_ID ) + and $d.snapshotHash = $r.snapshotHash + and $r.ID = 1 and $r.snapshotHash = 0 and exists ( - SELECT 1 from bookshop.WorklistItems as WorklistItems - where releaseChecks.parent_ID = WorklistItems.ID - and releaseChecks.parent_snapshotHash = WorklistItems.snapshotHash - and WorklistItems.ID = 1 and WorklistItems.snapshotHash = 0 + SELECT 1 from bookshop.WorklistItems as $W + where $r.parent_ID = $W.ID + and $r.parent_snapshotHash = $W.snapshotHash + and $W.ID = 1 and $W.snapshotHash = 0 ) ) and ( - detailsDeviations.ID = '0' - and detailsDeviations.snapshotHash = '0' - and detailsDeviations.batch_ID = '*' - and detailsDeviations.material_ID = '1' + $d.ID = '0' + and $d.snapshotHash = '0' + and $d.batch_ID = '*' + and $d.material_ID = '1' ) ` expect(cqn4sql(q, model)).to.deep.equal(expected) @@ -1352,19 +1352,19 @@ describe('Path expressions in from combined with `exists` predicate', () => { // SMW -> move that in a seperate "describe" ? // it('MUST ... mixed with path in FROM clause', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books:genre { ID } where exists parent`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books:genre as genre { ID } where exists parent`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Genres as genre { genre.ID } - WHERE EXISTS ( SELECT 1 from bookshop.Books as Books where Books.genre_ID = genre.ID ) - AND EXISTS ( SELECT 1 from bookshop.Genres as parent where parent.ID = genre.parent_ID ) + WHERE EXISTS ( SELECT 1 from bookshop.Books as $B where $B.genre_ID = genre.ID ) + AND EXISTS ( SELECT 1 from bookshop.Genres as $p where $p.ID = genre.parent_ID ) `) }) // semantically same as above it('MUST ... EXISTS in filter in FROM', () => { let query = cqn4sql(cds.ql`SELECT from bookshop.Books:genre[exists parent] { ID }`, model) - expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Genres as genre { genre.ID } - WHERE EXISTS ( SELECT 1 from bookshop.Books as Books where Books.genre_ID = genre.ID ) - AND EXISTS ( SELECT 1 from bookshop.Genres as parent where parent.ID = genre.parent_ID ) + expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Genres as $g { $g.ID } + WHERE EXISTS ( SELECT 1 from bookshop.Books as $B where $B.genre_ID = $g.ID ) + AND EXISTS ( SELECT 1 from bookshop.Genres as $p where $p.ID = $g.parent_ID ) `) }) }) @@ -1376,37 +1376,37 @@ describe('comparisons of associations in on condition of elements needs to be ex }) it('OData lambda where exists comparing managed assocs', () => { - const query = cqn4sql(cds.ql`SELECT from a2j.Foo { ID } where exists buz`, model) + const query = cqn4sql(cds.ql`SELECT from a2j.Foo as Foo { ID } where exists buz`, model) const expected = cds.ql` SELECT from a2j.Foo as Foo { Foo.ID } where exists ( - SELECT 1 FROM a2j.Buz as buz - where (buz.bar_ID = Foo.bar_ID AND buz.bar_foo_ID = Foo.bar_foo_ID) and buz.foo_ID = Foo.ID + SELECT 1 FROM a2j.Buz as $b + where ($b.bar_ID = Foo.bar_ID AND $b.bar_foo_ID = Foo.bar_foo_ID) and $b.foo_ID = Foo.ID ) ` expect(query).to.eql(expected) }) it('OData lambda where exists comparing managed assocs with renamed keys', () => { - const query = cqn4sql(cds.ql`SELECT from a2j.Foo { ID } where exists buzRenamed`, model) + const query = cqn4sql(cds.ql`SELECT from a2j.Foo as Foo { ID } where exists buzRenamed`, model) const expected = cds.ql` SELECT from a2j.Foo as Foo { Foo.ID } where exists ( - SELECT 1 FROM a2j.Buz as buzRenamed - where (buzRenamed.barRenamed_renameID = Foo.barRenamed_renameID AND buzRenamed.barRenamed_foo_ID = Foo.barRenamed_foo_ID) and buzRenamed.foo_ID = Foo.ID + SELECT 1 FROM a2j.Buz as $b + where ($b.barRenamed_renameID = Foo.barRenamed_renameID AND $b.barRenamed_foo_ID = Foo.barRenamed_foo_ID) and $b.foo_ID = Foo.ID ) ` expect(query).to.eql(expected) }) it('OData lambda where exists with unmanaged assoc', () => { - const query = cqn4sql(cds.ql`SELECT from a2j.Foo { ID } where exists buzUnmanaged`, model) + const query = cqn4sql(cds.ql`SELECT from a2j.Foo as Foo { ID } where exists buzUnmanaged`, model) const expected = cds.ql` SELECT from a2j.Foo as Foo { Foo.ID } where exists ( - SELECT 1 FROM a2j.Buz as buzUnmanaged - where buzUnmanaged.bar_foo_ID = Foo.bar_foo_ID AND buzUnmanaged.bar_ID = Foo.bar_ID and buzUnmanaged.foo_ID = Foo.ID + SELECT 1 FROM a2j.Buz as $b + where $b.bar_foo_ID = Foo.bar_foo_ID AND $b.bar_ID = Foo.bar_ID and $b.foo_ID = Foo.ID ) ` expect(query).to.eql(expected) @@ -1458,33 +1458,33 @@ describe('path expression within infix filter following exists predicate', () => }) it('via managed association', () => { - let query = cds.ql`SELECT from bookshop.Authors { ID } where exists books[genre.name = 'Thriller']` + let query = cds.ql`SELECT from bookshop.Authors as Authors { ID } where exists books[genre.name = 'Thriller']` const transformed = cqn4sql(query, model) expect(transformed).to.deep.equal( cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books - inner join bookshop.Genres as genre on genre.ID = books.genre_ID - where books.author_ID = Authors.ID and genre.name = 'Thriller' + SELECT 1 from bookshop.Books as $b + inner join bookshop.Genres as genre on genre.ID = $b.genre_ID + where $b.author_ID = Authors.ID and genre.name = 'Thriller' )`, ) }) it('via managed association multiple assocs', () => { - let query = cds.ql`SELECT from bookshop.Authors { ID } where exists books.author.books[genre.parent.name = 'Thriller']` + let query = cds.ql`SELECT from bookshop.Authors as Authors { ID } where exists books.author.books[genre.parent.name = 'Thriller']` const transformed = cqn4sql(query, model) expect(transformed).to.deep.equal( cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books - where books.author_ID = Authors.ID and EXISTS ( + SELECT 1 from bookshop.Books as $b + where $b.author_ID = Authors.ID and EXISTS ( - SELECT 1 from bookshop.Authors as author - where author.ID = books.author_ID and EXISTS ( + SELECT 1 from bookshop.Authors as $a + where $a.ID = $b.author_ID and EXISTS ( - SELECT 1 from bookshop.Books as books2 - inner join bookshop.Genres as genre on genre.ID = books2.genre_ID + SELECT 1 from bookshop.Books as $b2 + inner join bookshop.Genres as genre on genre.ID = $b2.genre_ID inner join bookshop.Genres as parent on parent.ID = genre.parent_ID - where books2.author_ID = author.ID and parent.name = 'Thriller' + where $b2.author_ID = $a.ID and parent.name = 'Thriller' ) @@ -1493,62 +1493,62 @@ describe('path expression within infix filter following exists predicate', () => ) }) it('via managed association, hidden in a function', () => { - let query = cds.ql`SELECT from bookshop.Authors { ID } where exists books[toLower(genre.name) = 'thriller']` + let query = cds.ql`SELECT from bookshop.Authors as Authors { ID } where exists books[toLower(genre.name) = 'thriller']` const transformed = cqn4sql(query, model) expect(transformed).to.deep.equal( cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books - inner join bookshop.Genres as genre on genre.ID = books.genre_ID - where books.author_ID = Authors.ID and toLower(genre.name) = 'thriller' + SELECT 1 from bookshop.Books as $b + inner join bookshop.Genres as genre on genre.ID = $b.genre_ID + where $b.author_ID = Authors.ID and toLower(genre.name) = 'thriller' )`, ) }) it('via unmanaged association', () => { // match all authors which have co-authored at least one book with King - let query = cds.ql`SELECT from bookshop.Authors { ID } where exists books[coAuthorUnmanaged.name = 'King']` + let query = cds.ql`SELECT from bookshop.Authors as Authors { ID } where exists books[coAuthorUnmanaged.name = 'King']` const transformed = cqn4sql(query, model) expect(transformed).to.deep.equal( cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books - inner join bookshop.Authors as coAuthorUnmanaged on coAuthorUnmanaged.ID = books.coAuthor_ID_unmanaged - where books.author_ID = Authors.ID and coAuthorUnmanaged.name = 'King' + SELECT 1 from bookshop.Books as $b + inner join bookshop.Authors as coAuthorUnmanaged on coAuthorUnmanaged.ID = $b.coAuthor_ID_unmanaged + where $b.author_ID = Authors.ID and coAuthorUnmanaged.name = 'King' )`, ) }) it('nested exists', () => { - let query = cds.ql`SELECT from bookshop.Authors { ID } where exists books[toLower(genre.name) = 'thriller' and exists genre[parent.name = 'Fiction']]` + let query = cds.ql`SELECT from bookshop.Authors as Authors { ID } where exists books[toLower(genre.name) = 'thriller' and exists genre[parent.name = 'Fiction']]` const transformed = cqn4sql(query, model) expect(transformed).to.deep.equal( cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books - inner join bookshop.Genres as genre on genre.ID = books.genre_ID - where books.author_ID = Authors.ID and toLower(genre.name) = 'thriller' + SELECT 1 from bookshop.Books as $b + inner join bookshop.Genres as genre on genre.ID = $b.genre_ID + where $b.author_ID = Authors.ID and toLower(genre.name) = 'thriller' and EXISTS ( - SELECT 1 from bookshop.Genres as genre2 - inner join bookshop.Genres as parent on parent.ID = genre2.parent_ID - where genre2.ID = books.genre_ID and parent.name = 'Fiction' + SELECT 1 from bookshop.Genres as $g + inner join bookshop.Genres as parent on parent.ID = $g.parent_ID + where $g.ID = $b.genre_ID and parent.name = 'Fiction' ) )`, ) }) it('scoped query with nested exists', () => { - let query = cds.ql`SELECT from bookshop.Authors[exists books[genre.name LIKE '%Fiction']]:books { ID }` + let query = cds.ql`SELECT from bookshop.Authors[exists books[genre.name LIKE '%Fiction']]:books as books { ID }` const transformed = cqn4sql(query, model) expect(transformed).to.deep.equal( cds.ql`SELECT from bookshop.Books as books { books.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Authors as Authors where Authors.ID = books.author_ID and + SELECT 1 from bookshop.Authors as $A where $A.ID = books.author_ID and EXISTS ( - SELECT 1 from bookshop.Books as books2 - inner join bookshop.Genres as genre on genre.ID = books2.genre_ID - where books2.author_ID = Authors.ID and genre.name LIKE '%Fiction' + SELECT 1 from bookshop.Books as $b + inner join bookshop.Genres as genre on genre.ID = $b.genre_ID + where $b.author_ID = $A.ID and genre.name LIKE '%Fiction' ) )`, ) @@ -1571,7 +1571,7 @@ describe('path expression within infix filter following exists predicate', () => it('in case statements', () => { // TODO: Aliases for genre could be improved let query = cqn4sql( - cds.ql`SELECT from bookshop.Authors + cds.ql`SELECT from bookshop.Authors as Authors { ID, case when exists books[toLower(genre.name) = 'Thriller' and price>10] then 1 when exists books[toLower(genre.name) = 'Thriller' and price>100 and exists genre] then 2 @@ -1584,17 +1584,17 @@ describe('path expression within infix filter following exists predicate', () => { Authors.ID, case when exists ( - select 1 from bookshop.Books as books - inner join bookshop.Genres as genre on genre.ID = books.genre_ID - where books.author_ID = Authors.ID and toLower(genre.name) = 'Thriller' and books.price > 10 + select 1 from bookshop.Books as $b + inner join bookshop.Genres as genre on genre.ID = $b.genre_ID + where $b.author_ID = Authors.ID and toLower(genre.name) = 'Thriller' and $b.price > 10 ) then 1 when exists ( - select 1 from bookshop.Books as books2 - inner join bookshop.Genres as genre on genre.ID = books2.genre_ID - where books2.author_ID = Authors.ID and toLower(genre.name) = 'Thriller' and books2.price > 100 + select 1 from bookshop.Books as $b2 + inner join bookshop.Genres as genre on genre.ID = $b2.genre_ID + where $b2.author_ID = Authors.ID and toLower(genre.name) = 'Thriller' and $b2.price > 100 and exists ( - select 1 from bookshop.Genres as genre2 where genre2.ID = books2.genre_ID + select 1 from bookshop.Genres as $g where $g.ID = $b2.genre_ID ) ) then 2 @@ -1606,16 +1606,16 @@ describe('path expression within infix filter following exists predicate', () => it('assoc is defined within a structure', () => { expect( cqn4sql( - cds.ql`SELECT from bookshop.Authors { ID } WHERE EXISTS books[toLower(toUpper(dedication.addressee.name)) = 'Hasso']`, + cds.ql`SELECT from bookshop.Authors as Authors { ID } WHERE EXISTS books[toLower(toUpper(dedication.addressee.name)) = 'Hasso']`, model, ), ).to.eql( cds.ql`SELECT from bookshop.Authors as Authors { Authors.ID } WHERE EXISTS ( - SELECT 1 from bookshop.Books as books + SELECT 1 from bookshop.Books as $b inner join bookshop.Person as addressee - on addressee.ID = books.dedication_addressee_ID - where books.author_ID = Authors.ID AND toLower(toUpper(addressee.name)) = 'Hasso' + on addressee.ID = $b.dedication_addressee_ID + where $b.author_ID = Authors.ID AND toLower(toUpper(addressee.name)) = 'Hasso' )`, ) }) diff --git a/db-service/test/cqn4sql/wildcards.test.js b/db-service/test/cqn4sql/wildcards.test.js index 63308f495..bb7851a3b 100644 --- a/db-service/test/cqn4sql/wildcards.test.js +++ b/db-service/test/cqn4sql/wildcards.test.js @@ -29,7 +29,7 @@ describe('wildcard expansion and exclude clause', () => { it('Respects excluding when expanding wildcard', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { *, author.ID as author } excluding {createdBy, modifiedBy}`, + cds.ql`SELECT from bookshop.Books as Books { *, author.ID as author } excluding {createdBy, modifiedBy}`, model, ) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { @@ -54,7 +54,7 @@ describe('wildcard expansion and exclude clause', () => { }) it('MUST respect smart wildcard rules', () => { - const input = cds.ql`SELECT from bookshop.Bar { 'first' as first, 'second' as createdAt, *, 'third' as ID }` + const input = cds.ql`SELECT from bookshop.Bar as Bar { 'first' as first, 'second' as createdAt, *, 'third' as ID }` let query = cqn4sql(input, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Bar as Bar { @@ -74,7 +74,7 @@ describe('wildcard expansion and exclude clause', () => { ) }) it('overwrite column with struct', () => { - const input = cds.ql`SELECT from bookshop.Bar { structure as first, 'second' as createdAt, *, structure as ID }` + const input = cds.ql`SELECT from bookshop.Bar as Bar { structure as first, 'second' as createdAt, *, structure as ID }` let query = cqn4sql(input, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Bar as Bar { @@ -97,7 +97,7 @@ describe('wildcard expansion and exclude clause', () => { }) it('MUST respect smart wildcard rules -> structure replacement before star', () => { - const input = cds.ql`SELECT from bookshop.Bar { 'first' as structure, * }` + const input = cds.ql`SELECT from bookshop.Bar as Bar { 'first' as structure, * }` let query = cqn4sql(input, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Bar as Bar { @@ -115,7 +115,7 @@ describe('wildcard expansion and exclude clause', () => { ) }) it('MUST respect smart wildcard rules -> structure replacement after star', () => { - const input = cds.ql`SELECT from bookshop.Bar { *, 'third' as structure }` + const input = cds.ql`SELECT from bookshop.Bar as Bar { *, 'third' as structure }` let query = cqn4sql(input, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Bar as Bar { @@ -133,7 +133,7 @@ describe('wildcard expansion and exclude clause', () => { ) }) it('MUST respect smart wildcard rules -> ref replaces wildcard element', () => { - const input = cds.ql`SELECT from bookshop.Bar { *, nested.bar.a as structure }` + const input = cds.ql`SELECT from bookshop.Bar as Bar { *, nested.bar.a as structure }` let query = cqn4sql(input, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Bar as Bar { @@ -154,22 +154,22 @@ describe('wildcard expansion and exclude clause', () => { const input = cds.ql`SELECT from bookshop.Bar { *, (SELECT from bookshop.Bar {ID}) as structure }` let query = cqn4sql(input, model) expect(query).to.deep.equal( - cds.ql`SELECT from bookshop.Bar as Bar { - Bar.ID, - Bar.stock, - (SELECT from bookshop.Bar as Bar2 {Bar2.ID}) as structure, - Bar.nested_foo_x, - Bar.nested_bar_a, - Bar.nested_bar_b, - Bar.note, - Bar.createdAt, - Bar.struct1_foo, - Bar.nested1_foo_x + cds.ql`SELECT from bookshop.Bar as $B { + $B.ID, + $B.stock, + (SELECT from bookshop.Bar as $B2 {$B2.ID}) as structure, + $B.nested_foo_x, + $B.nested_bar_a, + $B.nested_bar_b, + $B.note, + $B.createdAt, + $B.struct1_foo, + $B.nested1_foo_x }`, ) }) it('expand after wildcard overwrites assoc from wildcard expansion', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { *, author {name} }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { *, author {name} }`, model) expect(JSON.parse(JSON.stringify(query))).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.createdAt, @@ -181,9 +181,9 @@ describe('wildcard expansion and exclude clause', () => { Books.title, Books.descr, ( - SELECT from bookshop.Authors as author { - author.name - } where Books.author_ID = author.ID + SELECT from bookshop.Authors as $a { + $a.name + } where Books.author_ID = $a.ID ) as author, Books.coAuthor_ID, Books.genre_ID, @@ -200,7 +200,7 @@ describe('wildcard expansion and exclude clause', () => { }) it('expand after wildcard combines assoc from wildcard expansion (flat mode)', () => { const flatModel = cds.compile.for.nodejs(JSON.parse(JSON.stringify(model))) - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { *, author {name} }`, flatModel) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { *, author { name } }`, flatModel) const expected = cds.ql`SELECT from bookshop.Books as Books { Books.createdAt, @@ -223,9 +223,9 @@ describe('wildcard expansion and exclude clause', () => { Books.dedication_dedication, Books.coAuthor_ID_unmanaged, ( - SELECT from bookshop.Authors as author { - author.name - } where Books.author_ID = author.ID + SELECT from bookshop.Authors as $a { + $a.name + } where Books.author_ID = $a.ID ) as author } ` @@ -239,7 +239,7 @@ describe('wildcard expansion and exclude clause', () => { it('path expression after wildcard replaces assoc from wildcard expansion', () => { // "author.name as author" will replace the "author" association from Books -> no fk here - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { *, author.name as author }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { *, author.name as author }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { @@ -266,7 +266,7 @@ describe('wildcard expansion and exclude clause', () => { `) }) it('xpr after wildcard replaces assoc from wildcard expansion', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { *, ('Stephen' || 'King') as author }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { *, ('Stephen' || 'King') as author }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.createdAt, @@ -292,7 +292,7 @@ describe('wildcard expansion and exclude clause', () => { `) }) it('val after wildcard replaces assoc from wildcard expansion', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Books { *, 'King' as author }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books { *, 'King' as author }`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Books as Books { Books.createdAt, @@ -319,7 +319,7 @@ describe('wildcard expansion and exclude clause', () => { }) it('If a shadowed column is excluded, the shadowing column is inserted where defined', () => { - const input = cds.ql`SELECT from bookshop.Bar { 'first' as first, 'second' as createdAt, *, 'last' as ID } excluding { ID }` + const input = cds.ql`SELECT from bookshop.Bar as Bar { 'first' as first, 'second' as createdAt, *, 'last' as ID } excluding { ID }` let query = cqn4sql(input, model) // original query is prototype of transformed query -> JSON.parse(…) expect(JSON.parse(JSON.stringify(query))).to.deep.equal( @@ -355,7 +355,7 @@ describe('wildcard expansion and exclude clause', () => { }) it('MUST transform wildcard into explicit column refs (1)', () => { - const input = cds.ql`SELECT from bookshop.Bar { * }` + const input = cds.ql`SELECT from bookshop.Bar as Bar { * }` const inputClone = JSON.parse(JSON.stringify(input)) let query = cqn4sql(input, model) expect(query).to.deep.equal( @@ -379,11 +379,11 @@ describe('wildcard expansion and exclude clause', () => { it('MUST transform wildcard into explicit column refs (2)', () => { let starExpansion = cqn4sql( - cds.ql`SELECT from bookshop.Books { * }`, // resolve star into cols already + cds.ql`SELECT from bookshop.Books as Books { * }`, // resolve star into cols already model, ) let noColumnsExpansion = cqn4sql( - cds.ql`SELECT from bookshop.Books`, // resolve star into cols already + cds.ql`SELECT from bookshop.Books as Books`, model, ) const expected = cds.ql`SELECT from bookshop.Books as Books { @@ -413,11 +413,11 @@ describe('wildcard expansion and exclude clause', () => { it('MUST transform wildcard into explicit column refs (3)', () => { let starExpansion = cqn4sql( - cds.ql`SELECT from bookshop.Bar`, // resolve star into cols already + cds.ql`SELECT from bookshop.Bar as Bar `, model, ) let noColumnsExpansion = cqn4sql( - cds.ql`SELECT from bookshop.Bar`, // resolve star into cols already + cds.ql`SELECT from bookshop.Bar as Bar `, model, ) expect(starExpansion).to.deep.equal( @@ -454,7 +454,7 @@ describe('wildcard expansion and exclude clause', () => { it('MUST transform wildcard into explicit column refs and respect order', () => { let query = cqn4sql( - cds.ql`SELECT from bookshop.Bar {structure as beforeStar, *, structure as afterStar}`, // resolve star into cols already + cds.ql`SELECT from bookshop.Bar as Bar {structure as beforeStar, *, structure as afterStar}`, // resolve star into cols already model, ) expect(query).to.deep.equal( @@ -513,7 +513,7 @@ describe('wildcard expansion and exclude clause', () => { }) it('must not yield duplicate columns for already expanded foreign keys with OData CSN input', () => { const flatModel = cds.linked(cds.compile.for.nodejs(JSON.parse(JSON.stringify(model)))) - let query = cqn4sql(cds.ql`SELECT from bookshop.Books where author.name = 'Sanderson'`, flatModel) + let query = cqn4sql(cds.ql`SELECT from bookshop.Books as Books where author.name = 'Sanderson'`, flatModel) const expected = cds.ql`SELECT from bookshop.Books as Books left outer join bookshop.Authors as author on author.ID = Books.author_ID { @@ -549,7 +549,7 @@ describe('wildcard expansion and exclude clause', () => { it('must be possible to select already expanded foreign keys with OData CSN input', () => { const flatModel = cds.linked(cds.compile.for.nodejs(JSON.parse(JSON.stringify(model)))) let query = cqn4sql( - cds.ql`SELECT from bookshop.Books { genre_ID, author_ID } where author.name = 'Sanderson'`, + cds.ql`SELECT from bookshop.Books as Books { genre_ID, author_ID } where author.name = 'Sanderson'`, flatModel, ) const expected = cds.ql`SELECT from bookshop.Books as Books @@ -575,16 +575,16 @@ describe('wildcard expansion and exclude clause', () => { }) it('does not mistake "*" as wildcard in GROUP BY clause', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Bar { ID } group by '*'`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Bar as Bar { ID } group by '*'`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Bar as Bar { Bar.ID } group by '*'`) }) it('does not mistake "*" as wildcard in ORDER BY clause', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Bar { ID } order by '*'`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Bar as Bar { ID } order by '*'`, model) expect(query).to.deep.equal(cds.ql`SELECT from bookshop.Bar as Bar { Bar.ID } order by '*'`) }) it('ignores virtual field from wildcard expansion', () => { - let query = cqn4sql(cds.ql`SELECT from bookshop.Foo { * }`, model) + let query = cqn4sql(cds.ql`SELECT from bookshop.Foo as Foo { * }`, model) expect(query).to.deep.equal( cds.ql`SELECT from bookshop.Foo as Foo { Foo.ID, Foo.toFoo_ID, Foo.stru_u, Foo.stru_nested_nu }`, ) diff --git a/db-service/test/cqn4sql/with-parameters.test.js b/db-service/test/cqn4sql/with-parameters.test.js index d370de640..0db5300aa 100644 --- a/db-service/test/cqn4sql/with-parameters.test.js +++ b/db-service/test/cqn4sql/with-parameters.test.js @@ -16,12 +16,15 @@ describe('entities and views with parameters', () => { describe('associations to joins', () => { it('select from view with param', () => { - const query = cqn4sql(SELECT.from('PBooks(P1: 1, P2: 2)').columns('ID'), model) - const expected = SELECT.from('PBooks(P1: 1, P2: 2) as PBooks').columns('PBooks.ID') + // const query = cqn4sql(SELECT.from('PBooks(P1: 1, P2: 2)').columns('ID'), model) + const query = cqn4sql(cds.ql`SELECT from PBooks(P1: 1, P2: 2) as PBooks { ID }`, model) + const expected = cds.ql`SELECT from PBooks(P1: 1, P2: 2) as PBooks { PBooks.ID }` expect(query).to.deep.equal(expected) }) it('follow association to entity with params', () => { - const query = cqn4sql(SELECT.from('Books').columns('author(P1: 1, P2: 2).name as author'), model) + const query = cqn4sql(cds.ql`SELECT from Books as Books { + author(P1: 1, P2: 2).name as author + }`, model) const expected = cds.ql` SELECT FROM Books as Books left join Authors(P1:1, P2: 2) as author on author.ID = Books.author_ID { @@ -31,7 +34,10 @@ describe('entities and views with parameters', () => { expect(query).to.deep.equal(expected) }) it('select from entity with params and follow association to entity with params', () => { - const query = cqn4sql(SELECT.from('PBooks(P1: 42, P2: 45)').columns('author(P1: 1, P2: 2).name as author'), model) + const query = cqn4sql(cds.ql` + SELECT from PBooks(P1: 42, P2: 45) as PBooks { + author(P1: 1, P2: 2).name as author + }`, model) const expected = cds.ql` SELECT FROM PBooks(P1: 42, P2: 45) as PBooks left join Authors(P1:1, P2: 2) as author on author.ID = PBooks.author_ID { @@ -41,7 +47,7 @@ describe('entities and views with parameters', () => { expect(query).to.deep.equal(expected) }) it('join identity via params', () => { - const cqn = cds.ql`SELECT from PBooks(P1: 42, P2: 45) { + const cqn = cds.ql`SELECT from PBooks(P1: 42, P2: 45) as PBooks { author(P1: 1, P2: 2).name as author, author(P1: 1, P2: 2).name as sameAuthor, @@ -68,7 +74,7 @@ describe('entities and views with parameters', () => { expect(query).to.deep.equal(expected) }) it('empty argument list if no params provided for association', () => { - const cqn = cds.ql`SELECT from PBooks(P1: 42, P2: 45) { + const cqn = cds.ql`SELECT from PBooks(P1: 42, P2: 45) as PBooks { author.name as author, }` const query = cqn4sql(cqn, model) @@ -84,7 +90,7 @@ describe('entities and views with parameters', () => { expect(query).to.deep.equal(expected) }) it('empty argument list if no params provided for entity and association', () => { - const cqn = cds.ql`SELECT from PBooks { + const cqn = cds.ql`SELECT from PBooks as PBooks { author.name as author, }` const query = cqn4sql(cqn, model) @@ -101,7 +107,7 @@ describe('entities and views with parameters', () => { expect(query).to.deep.equal(expected) }) it('empty argument list for UDF', () => { - const cqn = cds.ql`SELECT from BooksUDF { + const cqn = cds.ql`SELECT from BooksUDF as BooksUDF { author.name as author, }` const query = cqn4sql(cqn, model) @@ -121,31 +127,31 @@ describe('entities and views with parameters', () => { describe('where exists', () => { it('scoped query', () => { - const query = cds.ql`SELECT from Books:author(P1: 1, P2: 2) { ID }` + const query = cds.ql`SELECT from Books:author(P1: 1, P2: 2) as author { ID }` const expected = cds.ql` SELECT from Authors(P1: 1, P2: 2) as author { author.ID } where exists ( - SELECT 1 from Books as Books where Books.author_ID = author.ID + SELECT 1 from Books as $B where $B.author_ID = author.ID ) ` expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('where exists shortcut', () => { - const query = cds.ql`SELECT from Books { ID } where exists author(P1: 1, P2: 2)` + const query = cds.ql`SELECT from Books as Books { ID } where exists author(P1: 1, P2: 2)` const expected = cds.ql` SELECT from Books as Books { Books.ID } where exists ( - SELECT 1 from Authors(P1: 1, P2: 2) as author where author.ID = Books.author_ID + SELECT 1 from Authors(P1: 1, P2: 2) as $a where $a.ID = Books.author_ID ) ` expect(cqn4sql(query, model)).to.deep.equal(expected) }) it('where exists shortcut w/o params', () => { - const query = cds.ql`SELECT from Books { ID } where exists author` + const query = cds.ql`SELECT from Books as Books { ID } where exists author` const expected = cds.ql` SELECT from Books as Books { Books.ID } where exists ( - SELECT 1 from Authors(P1: dummy) as author where author.ID = Books.author_ID + SELECT 1 from Authors(P1: dummy) as $a where $a.ID = Books.author_ID ) ` // manually remove the param from argument list because compiler does not allow empty args for cqn @@ -156,27 +162,27 @@ describe('entities and views with parameters', () => { describe('expand subqueries', () => { it('expand with params', () => { - const query = cds.ql`SELECT from Books { + const query = cds.ql`SELECT from Books as Books { author(P1: 1, P2: 2) { ID } }` const expected = cds.ql`SELECT from Books as Books { ( - SELECT from Authors(P1: 1, P2: 2) as author { - author.ID - } where Books.author_ID = author.ID + SELECT from Authors(P1: 1, P2: 2) as $a { + $a.ID + } where Books.author_ID = $a.ID ) as author }` expect(JSON.parse(JSON.stringify(cqn4sql(query, model)))).to.deep.equal(expected) }) it('expand on parameterized entity without args', () => { - const query = cds.ql`SELECT from Books { + const query = cds.ql`SELECT from Books as Books { author { ID } }` const expected = cds.ql`SELECT from Books as Books { ( - SELECT from Authors(P1: dummy) as author { - author.ID - } where Books.author_ID = author.ID + SELECT from Authors(P1: dummy) as $a { + $a.ID + } where Books.author_ID = $a.ID ) as author }` // manually remove the param from argument list because compiler does not allow empty args for cqn diff --git a/test/compliance/UPDATE.test.js b/test/compliance/UPDATE.test.js index 4f7830b22..1ab60af04 100644 --- a/test/compliance/UPDATE.test.js +++ b/test/compliance/UPDATE.test.js @@ -63,7 +63,7 @@ describe('UPDATE', () => { expect(0).to.be(1) } catch (error) { // nonExisting is filtered, so the sql is incomplete - expect(error.query).to.match(/UPDATE basic_literals_string AS ["]?string["]? SET [\n]?/i) + expect(error.query).to.match(/UPDATE basic_literals_string AS ["]?\$s["]? SET [\n]?/i) } }) })