@cap-js/db-service 1.16.2 → 1.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/lib/cql-functions.js +3 -3
- package/lib/cqn2sql.js +5 -1
- package/lib/cqn4sql.js +8 -5
- package/lib/deep-queries.js +1 -1
- package/lib/infer/index.js +33 -22
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,32 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [1.17.1](https://github.com/cap-js/cds-dbs/compare/db-service-v1.17.0...db-service-v1.17.1) (2025-02-04)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
* deep update resulting in deep delete of sub-element ([#1006](https://github.com/cap-js/cds-dbs/issues/1006)) ([ef2f817](https://github.com/cap-js/cds-dbs/commit/ef2f8175df6fc7076fa8a9290e1863f44d267d8d))
|
|
13
|
+
* nested $self reference to other column ([#1009](https://github.com/cap-js/cds-dbs/issues/1009)) ([41a76d8](https://github.com/cap-js/cds-dbs/commit/41a76d89a884ac8266ccbd2d087af435e8f26ccb))
|
|
14
|
+
|
|
15
|
+
## [1.17.0](https://github.com/cap-js/cds-dbs/compare/db-service-v1.16.2...db-service-v1.17.0) (2025-01-28)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
* support for cds.Map ([#889](https://github.com/cap-js/cds-dbs/issues/889)) ([cde7514](https://github.com/cap-js/cds-dbs/commit/cde7514df20396383e0179ffce838596e3706bb2))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
* **`UPDATE`:** no assocs in list which matches subquery results ([4bcb88a](https://github.com/cap-js/cds-dbs/commit/4bcb88a1f40540e26cebd4907bdd33e90d08bb9d))
|
|
26
|
+
* **`UPDATE`:** only perform subselect matching if necessary ([#989](https://github.com/cap-js/cds-dbs/issues/989)) ([4bcb88a](https://github.com/cap-js/cds-dbs/commit/4bcb88a1f40540e26cebd4907bdd33e90d08bb9d))
|
|
27
|
+
* contains not evaluting to bool ([#980](https://github.com/cap-js/cds-dbs/issues/980)) ([760484b](https://github.com/cap-js/cds-dbs/commit/760484be4cf3d0c755254e90f7740ba0b34b9249))
|
|
28
|
+
* nested ternary in calculated element ([#981](https://github.com/cap-js/cds-dbs/issues/981)) ([5f4a1fe](https://github.com/cap-js/cds-dbs/commit/5f4a1feed7b74bb1736f6140527e70b1e261f368))
|
|
29
|
+
* reject virtual elements in all expressions ([#972](https://github.com/cap-js/cds-dbs/issues/972)) ([d0c949d](https://github.com/cap-js/cds-dbs/commit/d0c949d8a3a9851ccd70b3f998caec0b5f01ce0e))
|
|
30
|
+
* starts endswith for null values ([#975](https://github.com/cap-js/cds-dbs/issues/975)) ([f0330bc](https://github.com/cap-js/cds-dbs/commit/f0330bc334fd3a8ed5377afcdd04b731baa8c753))
|
|
31
|
+
* use "$$value$$" as internal column name for UPSERT ([#976](https://github.com/cap-js/cds-dbs/issues/976)) ([8c86b86](https://github.com/cap-js/cds-dbs/commit/8c86b863a69833d50cff91483150bf0314bb7258))
|
|
32
|
+
|
|
7
33
|
## [1.16.2](https://github.com/cap-js/cds-dbs/compare/db-service-v1.16.1...db-service-v1.16.2) (2024-12-18)
|
|
8
34
|
|
|
9
35
|
|
package/lib/cql-functions.js
CHANGED
|
@@ -49,7 +49,7 @@ const StandardFunctions = {
|
|
|
49
49
|
* @param {...string} args
|
|
50
50
|
* @returns {string}
|
|
51
51
|
*/
|
|
52
|
-
contains: (...args) => `ifnull(instr(${args}),0)`,
|
|
52
|
+
contains: (...args) => `(ifnull(instr(${args}),0) > 0)`,
|
|
53
53
|
/**
|
|
54
54
|
* Generates SQL statement that produces the number of elements in a given collection
|
|
55
55
|
* @param {string} x
|
|
@@ -75,7 +75,7 @@ const StandardFunctions = {
|
|
|
75
75
|
* @param {string} y
|
|
76
76
|
* @returns {string}
|
|
77
77
|
*/
|
|
78
|
-
startswith: (x, y) => `instr(${x},${y}) = 1`, // sqlite instr is 1 indexed
|
|
78
|
+
startswith: (x, y) => `coalesce(instr(${x},${y}) = 1,false)`, // sqlite instr is 1 indexed
|
|
79
79
|
// takes the end of the string of the size of the target and compares it with the target
|
|
80
80
|
/**
|
|
81
81
|
* Generates SQL statement that produces a boolean value indicating whether the first string ends with the second string
|
|
@@ -83,7 +83,7 @@ const StandardFunctions = {
|
|
|
83
83
|
* @param {string} y
|
|
84
84
|
* @returns {string}
|
|
85
85
|
*/
|
|
86
|
-
endswith: (x, y) => `substr(${x}, length(${x}) + 1 - length(${y})) = ${y}`,
|
|
86
|
+
endswith: (x, y) => `coalesce(substr(${x}, length(${x}) + 1 - length(${y})) = ${y},false)`,
|
|
87
87
|
/**
|
|
88
88
|
* Generates SQL statement that produces the substring of a given string
|
|
89
89
|
* @example
|
package/lib/cqn2sql.js
CHANGED
|
@@ -197,6 +197,7 @@ class CQN2SQLRenderer {
|
|
|
197
197
|
Association: () => false,
|
|
198
198
|
Composition: () => false,
|
|
199
199
|
array: () => 'NCLOB',
|
|
200
|
+
Map: () => 'NCLOB',
|
|
200
201
|
// HANA types
|
|
201
202
|
'cds.hana.TINYINT': () => 'TINYINT',
|
|
202
203
|
'cds.hana.REAL': () => 'REAL',
|
|
@@ -744,7 +745,10 @@ class CQN2SQLRenderer {
|
|
|
744
745
|
.map(c => `${c.onInsert || c.sql} as ${this.quote(c.name)}`)
|
|
745
746
|
|
|
746
747
|
const entity = this.name(q.target?.name || UPSERT.into.ref[0], q)
|
|
747
|
-
sql = `SELECT ${managed.map(c => c.upsert
|
|
748
|
+
sql = `SELECT ${managed.map(c => c.upsert
|
|
749
|
+
.replace(/value->/g, '"$$$$value$$$$"->')
|
|
750
|
+
.replace(/json_type\(value,/g, 'json_type("$$$$value$$$$",'))
|
|
751
|
+
} FROM (SELECT value as "$$value$$", ${extractkeys} from json_each(?)) as NEW LEFT JOIN ${this.quote(entity)} AS OLD ON ${keyCompare}`
|
|
748
752
|
|
|
749
753
|
const updateColumns = columns.filter(c => {
|
|
750
754
|
if (keys.includes(c)) return false //> keys go into ON CONFLICT clause
|
package/lib/cqn4sql.js
CHANGED
|
@@ -134,7 +134,7 @@ function cqn4sql(originalQuery, model) {
|
|
|
134
134
|
const primaryKey = { list: [] }
|
|
135
135
|
for (const k of Object.keys(queryTarget.elements)) {
|
|
136
136
|
const e = queryTarget.elements[k]
|
|
137
|
-
if (e.key === true && !e.virtual) {
|
|
137
|
+
if (e.key === true && !e.virtual && e.isAssociation !== true) {
|
|
138
138
|
subquery.SELECT.columns.push({ ref: [e.name] })
|
|
139
139
|
primaryKey.list.push({ ref: [transformedFrom.as, e.name] })
|
|
140
140
|
}
|
|
@@ -1286,7 +1286,7 @@ function cqn4sql(originalQuery, model) {
|
|
|
1286
1286
|
}
|
|
1287
1287
|
}
|
|
1288
1288
|
return flatColumns
|
|
1289
|
-
} else if (element.elements) {
|
|
1289
|
+
} else if (element.elements && element.type !== 'cds.Map') {
|
|
1290
1290
|
const flatRefs = []
|
|
1291
1291
|
Object.values(element.elements).forEach(e => {
|
|
1292
1292
|
const alias = columnAlias ? `${columnAlias}_${e.name}` : null
|
|
@@ -1450,7 +1450,7 @@ function cqn4sql(originalQuery, model) {
|
|
|
1450
1450
|
transformedTokenStream.push({ ...token })
|
|
1451
1451
|
} else {
|
|
1452
1452
|
// expand `struct = null | struct2`
|
|
1453
|
-
const
|
|
1453
|
+
const definition = token.$refLinks?.at(-1).definition
|
|
1454
1454
|
const next = tokenStream[i + 1]
|
|
1455
1455
|
if (allOps.some(([firstOp]) => firstOp === next) && (definition?.elements || definition?.keys)) {
|
|
1456
1456
|
const ops = [next]
|
|
@@ -1477,6 +1477,9 @@ function cqn4sql(originalQuery, model) {
|
|
|
1477
1477
|
} else {
|
|
1478
1478
|
// reject associations in expression, except if we are in an infix filter -> $baseLink is set
|
|
1479
1479
|
assertNoStructInXpr(token, $baseLink)
|
|
1480
|
+
// reject virtual elements in expressions as they will lead to a sql error down the line
|
|
1481
|
+
if(definition?.virtual)
|
|
1482
|
+
throw new Error(`Virtual elements are not allowed in expressions`)
|
|
1480
1483
|
|
|
1481
1484
|
let result = is_regexp(token?.val) ? token : copy(token) // REVISIT: too expensive! //
|
|
1482
1485
|
if (token.ref) {
|
|
@@ -1620,10 +1623,10 @@ function cqn4sql(originalQuery, model) {
|
|
|
1620
1623
|
rejectStructInExpression()
|
|
1621
1624
|
|
|
1622
1625
|
function rejectAssocInExpression() {
|
|
1623
|
-
throw new Error(
|
|
1626
|
+
throw new Error(`An association can't be used as a value in an expression`)
|
|
1624
1627
|
}
|
|
1625
1628
|
function rejectStructInExpression() {
|
|
1626
|
-
throw new Error(
|
|
1629
|
+
throw new Error(`A structured element can't be used as a value in an expression`)
|
|
1627
1630
|
}
|
|
1628
1631
|
}
|
|
1629
1632
|
|
package/lib/deep-queries.js
CHANGED
|
@@ -51,7 +51,7 @@ async function onDeep(req, next) {
|
|
|
51
51
|
// - deletes never trigger unique constraints, but can prevent them -> execute first
|
|
52
52
|
// - updates can trigger and prevent unique constraints -> execute second
|
|
53
53
|
// - inserts can only trigger unique constraints -> execute last
|
|
54
|
-
await Promise.all(Array.from(queries.deletes.values()).map(query => this.
|
|
54
|
+
await Promise.all(Array.from(queries.deletes.values()).map(query => this.onDELETE({ query, target: query._target })))
|
|
55
55
|
await Promise.all(queries.updates.map(query => this.onUPDATE({ query })))
|
|
56
56
|
|
|
57
57
|
const rootQuery = queries.inserts.get(ROOT)
|
package/lib/infer/index.js
CHANGED
|
@@ -207,12 +207,12 @@ function infer(originalQuery, model) {
|
|
|
207
207
|
if (as === undefined) cds.error`Expecting expression to have an alias name`
|
|
208
208
|
if (queryElements[as]) cds.error`Duplicate definition of element “${as}”`
|
|
209
209
|
if (col.xpr || col.SELECT) {
|
|
210
|
-
queryElements[as] = getElementForXprOrSubquery(col, queryElements)
|
|
210
|
+
queryElements[as] = getElementForXprOrSubquery(col, queryElements, dollarSelfRefs)
|
|
211
211
|
}
|
|
212
212
|
if (col.func) {
|
|
213
213
|
if (col.args) {
|
|
214
214
|
// {func}.args are optional
|
|
215
|
-
applyToFunctionArgs(col.args, inferArg, [false])
|
|
215
|
+
applyToFunctionArgs(col.args, inferArg, [false, null, {dollarSelfRefs}])
|
|
216
216
|
}
|
|
217
217
|
queryElements[as] = getElementForCast(col)
|
|
218
218
|
}
|
|
@@ -287,7 +287,7 @@ function infer(originalQuery, model) {
|
|
|
287
287
|
if (having) walkTokenStream(having)
|
|
288
288
|
if (_.with)
|
|
289
289
|
// consider UPDATE.with
|
|
290
|
-
Object.values(_.with).forEach(val => inferArg(val, queryElements, null, {
|
|
290
|
+
Object.values(_.with).forEach(val => inferArg(val, queryElements, null, { inXpr: true }))
|
|
291
291
|
|
|
292
292
|
return queryElements
|
|
293
293
|
|
|
@@ -299,7 +299,7 @@ function infer(originalQuery, model) {
|
|
|
299
299
|
*
|
|
300
300
|
* @param {array} tokenStream
|
|
301
301
|
*/
|
|
302
|
-
function walkTokenStream(tokenStream,
|
|
302
|
+
function walkTokenStream(tokenStream, inXpr = false) {
|
|
303
303
|
let skipJoins
|
|
304
304
|
const processToken = t => {
|
|
305
305
|
if (t === 'exists') {
|
|
@@ -309,7 +309,7 @@ function infer(originalQuery, model) {
|
|
|
309
309
|
// don't miss an exists within an expression
|
|
310
310
|
t.xpr.forEach(processToken)
|
|
311
311
|
} else {
|
|
312
|
-
inferArg(t, queryElements, null, { inExists: skipJoins,
|
|
312
|
+
inferArg(t, queryElements, null, { inExists: skipJoins, inXpr, inQueryModifier: true })
|
|
313
313
|
skipJoins = false
|
|
314
314
|
}
|
|
315
315
|
}
|
|
@@ -329,11 +329,12 @@ function infer(originalQuery, model) {
|
|
|
329
329
|
const unprocessedColumns = []
|
|
330
330
|
|
|
331
331
|
for (const currentDollarSelfColumn of dollarSelfColumns) {
|
|
332
|
-
const { ref } = currentDollarSelfColumn
|
|
332
|
+
const { ref, inXpr } = currentDollarSelfColumn
|
|
333
333
|
const stepToFind = ref[1]
|
|
334
334
|
|
|
335
335
|
const referencesOtherDollarSelfColumn = dollarSelfColumns.find(
|
|
336
336
|
otherDollarSelfCol =>
|
|
337
|
+
!(stepToFind in queryElements) &&
|
|
337
338
|
otherDollarSelfCol !== currentDollarSelfColumn &&
|
|
338
339
|
(otherDollarSelfCol.as
|
|
339
340
|
? stepToFind === otherDollarSelfCol.as
|
|
@@ -343,7 +344,7 @@ function infer(originalQuery, model) {
|
|
|
343
344
|
if (referencesOtherDollarSelfColumn) {
|
|
344
345
|
unprocessedColumns.push(currentDollarSelfColumn)
|
|
345
346
|
} else {
|
|
346
|
-
handleRef(currentDollarSelfColumn)
|
|
347
|
+
handleRef(currentDollarSelfColumn, inXpr)
|
|
347
348
|
}
|
|
348
349
|
}
|
|
349
350
|
|
|
@@ -351,8 +352,8 @@ function infer(originalQuery, model) {
|
|
|
351
352
|
} while (dollarSelfColumns.length > 0)
|
|
352
353
|
}
|
|
353
354
|
|
|
354
|
-
function handleRef(col) {
|
|
355
|
-
inferArg(col, queryElements)
|
|
355
|
+
function handleRef(col, inXpr) {
|
|
356
|
+
inferArg(col, queryElements, null, { inXpr })
|
|
356
357
|
const { definition } = col.$refLinks[col.$refLinks.length - 1]
|
|
357
358
|
if (col.cast)
|
|
358
359
|
// final type overwritten -> element not visible anymore
|
|
@@ -379,7 +380,7 @@ function infer(originalQuery, model) {
|
|
|
379
380
|
* @param {object} [context={}] - Contextual information for element inference.
|
|
380
381
|
* @param {boolean} [context.inExists=false] - Flag to control the creation of joins for non-association path traversals.
|
|
381
382
|
* for `exists <assoc>` paths we do not need to create joins for path expressions as they are part of the semi-joined subquery.
|
|
382
|
-
* @param {boolean} [context.
|
|
383
|
+
* @param {boolean} [context.inXpr=false] - Flag to signal whether the element is part of an expression.
|
|
383
384
|
* Used to ignore non-persisted elements.
|
|
384
385
|
* @param {boolean} [context.inNestedProjection=false] - Flag to signal whether the element is part of a nested projection.
|
|
385
386
|
*
|
|
@@ -401,11 +402,11 @@ function infer(originalQuery, model) {
|
|
|
401
402
|
*/
|
|
402
403
|
|
|
403
404
|
function inferArg(arg, queryElements = null, $baseLink = null, context = {}) {
|
|
404
|
-
const { inExists,
|
|
405
|
+
const { inExists, inXpr, inCalcElement, baseColumn, inInfixFilter, inQueryModifier, inFrom, dollarSelfRefs } = context
|
|
405
406
|
if (arg.param || arg.SELECT) return // parameter references are only resolved into values on execution e.g. :val, :1 or ?
|
|
406
407
|
if (arg.args) applyToFunctionArgs(arg.args, inferArg, [null, $baseLink, context])
|
|
407
408
|
if (arg.list) arg.list.forEach(arg => inferArg(arg, null, $baseLink, context))
|
|
408
|
-
if (arg.xpr) arg.xpr.forEach(token => inferArg(token, queryElements, $baseLink, { ...context,
|
|
409
|
+
if (arg.xpr) arg.xpr.forEach(token => inferArg(token, queryElements, $baseLink, { ...context, inXpr: true })) // e.g. function in expression
|
|
409
410
|
|
|
410
411
|
if (!arg.ref) {
|
|
411
412
|
if (arg.expand && queryElements) queryElements[arg.as] = resolveExpand(arg)
|
|
@@ -426,6 +427,11 @@ function infer(originalQuery, model) {
|
|
|
426
427
|
firstStepIsSelf = !firstStepIsTableAlias && arg.ref.length > 1 && ['$self', '$projection'].includes(arg.ref[0])
|
|
427
428
|
expandOnTableAlias = arg.ref.length === 1 && arg.ref[0] in sources && (arg.expand || arg.inline)
|
|
428
429
|
}
|
|
430
|
+
if(dollarSelfRefs && firstStepIsSelf) {
|
|
431
|
+
Object.defineProperty(arg, 'inXpr', { value: true, writable: true })
|
|
432
|
+
dollarSelfRefs.push(arg)
|
|
433
|
+
return
|
|
434
|
+
}
|
|
429
435
|
const nameSegments = []
|
|
430
436
|
// if a (segment) of a (structured) foreign key is renamed, we must not include
|
|
431
437
|
// the aliased ref segments into the name of the final foreign key which is e.g. used in
|
|
@@ -564,7 +570,7 @@ function infer(originalQuery, model) {
|
|
|
564
570
|
} else if (token.ref || token.xpr || token.list) {
|
|
565
571
|
inferArg(token, false, arg.$refLinks[i], {
|
|
566
572
|
inExists: skipJoinsForFilter || inExists,
|
|
567
|
-
|
|
573
|
+
inXpr: !!token.xpr,
|
|
568
574
|
inInfixFilter: true,
|
|
569
575
|
inFrom,
|
|
570
576
|
})
|
|
@@ -573,7 +579,7 @@ function infer(originalQuery, model) {
|
|
|
573
579
|
applyToFunctionArgs(token.args, inferArg, [
|
|
574
580
|
false,
|
|
575
581
|
arg.$refLinks[i],
|
|
576
|
-
{ inExists: skipJoinsForFilter || inExists,
|
|
582
|
+
{ inExists: skipJoinsForFilter || inExists, inXpr: true, inInfixFilter: true, inFrom },
|
|
577
583
|
])
|
|
578
584
|
}
|
|
579
585
|
}
|
|
@@ -641,7 +647,7 @@ function infer(originalQuery, model) {
|
|
|
641
647
|
}
|
|
642
648
|
}
|
|
643
649
|
const leafArt = arg.$refLinks[arg.$refLinks.length - 1].definition
|
|
644
|
-
const virtual = (leafArt.virtual || !isPersisted) && !
|
|
650
|
+
const virtual = (leafArt.virtual || !isPersisted) && !inXpr
|
|
645
651
|
// check if we need to merge the column `ref` into the join tree of the query
|
|
646
652
|
if (!inFrom && !inExists && !virtual && !inCalcElement) {
|
|
647
653
|
// for a ref inside an `inline` we need to consider the column `ref` which has the `inline` prop
|
|
@@ -658,7 +664,7 @@ function infer(originalQuery, model) {
|
|
|
658
664
|
}
|
|
659
665
|
|
|
660
666
|
function insertIntoQueryElements() {
|
|
661
|
-
return queryElements && !
|
|
667
|
+
return queryElements && !inXpr && !inInfixFilter && !inQueryModifier
|
|
662
668
|
}
|
|
663
669
|
|
|
664
670
|
/**
|
|
@@ -686,7 +692,7 @@ function infer(originalQuery, model) {
|
|
|
686
692
|
}
|
|
687
693
|
let elements = {}
|
|
688
694
|
inline.forEach(inlineCol => {
|
|
689
|
-
inferArg(inlineCol, null, $leafLink, {
|
|
695
|
+
inferArg(inlineCol, null, $leafLink, { inXpr: true, baseColumn: col })
|
|
690
696
|
if (inlineCol === '*') {
|
|
691
697
|
const wildCardElements = {}
|
|
692
698
|
// either the `.elements´ of the struct or the `.elements` of the assoc target
|
|
@@ -762,7 +768,7 @@ function infer(originalQuery, model) {
|
|
|
762
768
|
if (e === '*') {
|
|
763
769
|
elements = { ...elements, ...$leafLink.definition.elements }
|
|
764
770
|
} else {
|
|
765
|
-
inferArg(e, false, $leafLink, {
|
|
771
|
+
inferArg(e, false, $leafLink, { inXpr: true })
|
|
766
772
|
if (e.expand) elements[e.as || e.flatName] = resolveExpand(e)
|
|
767
773
|
if (e.inline) elements = { ...elements, ...resolveInline(e) }
|
|
768
774
|
else elements[e.as || e.flatName] = e.$refLinks ? e.$refLinks[e.$refLinks.length - 1].definition : e
|
|
@@ -859,7 +865,7 @@ function infer(originalQuery, model) {
|
|
|
859
865
|
} else if (arg.xpr || arg.args) {
|
|
860
866
|
const prop = arg.xpr ? 'xpr' : 'args'
|
|
861
867
|
arg[prop].forEach(step => {
|
|
862
|
-
|
|
868
|
+
let subPath = { $refLinks: [...basePath.$refLinks], ref: [...basePath.ref] }
|
|
863
869
|
if (step.ref) {
|
|
864
870
|
step.$refLinks.forEach((link, i) => {
|
|
865
871
|
const { definition } = link
|
|
@@ -874,6 +880,10 @@ function infer(originalQuery, model) {
|
|
|
874
880
|
} else if (step.args || step.xpr) {
|
|
875
881
|
const nestedProp = step.xpr ? 'xpr' : 'args'
|
|
876
882
|
step[nestedProp].forEach(a => {
|
|
883
|
+
// reset sub path for each nested argument
|
|
884
|
+
// e.g. case when <path> then <otherPath> else <anotherPath> end
|
|
885
|
+
if(!a.ref)
|
|
886
|
+
subPath = { $refLinks: [...basePath.$refLinks], ref: [...basePath.ref] }
|
|
877
887
|
mergePathsIntoJoinTree(a, subPath)
|
|
878
888
|
})
|
|
879
889
|
}
|
|
@@ -959,7 +969,8 @@ function infer(originalQuery, model) {
|
|
|
959
969
|
if (element.type !== 'cds.LargeBinary') {
|
|
960
970
|
queryElements[k] = element
|
|
961
971
|
}
|
|
962
|
-
if
|
|
972
|
+
// only relevant if we actually select the calculated element
|
|
973
|
+
if (originalQuery.SELECT && isCalculatedOnRead(element)) {
|
|
963
974
|
linkCalculatedElement(element)
|
|
964
975
|
}
|
|
965
976
|
}
|
|
@@ -1003,7 +1014,7 @@ function infer(originalQuery, model) {
|
|
|
1003
1014
|
* @param {object} col
|
|
1004
1015
|
* @returns object
|
|
1005
1016
|
*/
|
|
1006
|
-
function getElementForXprOrSubquery(col, queryElements) {
|
|
1017
|
+
function getElementForXprOrSubquery(col, queryElements, dollarSelfRefs) {
|
|
1007
1018
|
const { xpr } = col
|
|
1008
1019
|
let skipJoins = false
|
|
1009
1020
|
xpr?.forEach(token => {
|
|
@@ -1011,7 +1022,7 @@ function infer(originalQuery, model) {
|
|
|
1011
1022
|
// no joins for infix filters along `exists <path>`
|
|
1012
1023
|
skipJoins = true
|
|
1013
1024
|
} else {
|
|
1014
|
-
inferArg(token, queryElements, null, { inExists: skipJoins,
|
|
1025
|
+
inferArg(token, queryElements, null, { inExists: skipJoins, inXpr: true, dollarSelfRefs })
|
|
1015
1026
|
skipJoins = false
|
|
1016
1027
|
}
|
|
1017
1028
|
})
|
package/package.json
CHANGED