@sap/cds-compiler 6.4.2 → 6.4.6
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 +55 -5
- package/lib/base/message-registry.js +2 -2
- package/lib/checks/existsExpressionsOnlyForeignKeys.js +16 -10
- package/lib/checks/existsMustEndInAssoc.js +1 -1
- package/lib/checks/existsMustNotStartWithDollarSelf.js +31 -0
- package/lib/checks/validator.js +4 -2
- package/lib/compiler/assert-consistency.js +2 -2
- package/lib/compiler/builtins.js +5 -6
- package/lib/compiler/shared.js +127 -109
- package/lib/compiler/tweak-assocs.js +1 -1
- package/lib/compiler/xpr-rewrite.js +111 -139
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +3 -2
- package/lib/parsers/AstBuildingParser.js +1 -1
- package/lib/transform/db/assocsToQueries/transformExists.js +3 -10
- package/lib/transform/db/assocsToQueries/utils.js +0 -5
- package/lib/transform/localized.js +13 -20
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,9 +4,35 @@
|
|
|
4
4
|
<!-- markdownlint-disable MD004 -->
|
|
5
5
|
<!-- (no-duplicate-heading)-->
|
|
6
6
|
|
|
7
|
-
Note: `beta`
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Note: while we list new `beta` flags and their removal in this ChangeLog,
|
|
8
|
+
we might not list every change in its behavior here.
|
|
9
|
+
Productive code should never require a `beta` flag to be set, and
|
|
10
|
+
might use a deprecated flag only for a limited period of time.
|
|
11
|
+
|
|
12
|
+
## Version 6.4.6 - 2025-10-23
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- compiler: a references to an element of the target in a filter for associations
|
|
17
|
+
inside an annotation expression does not lead to a compiler message requesting
|
|
18
|
+
users to provide the annotation themselves (regression with v6.4.4)
|
|
19
|
+
|
|
20
|
+
## Version 6.4.4 - 2025-10-15
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- compiler:
|
|
25
|
+
+ properly rewrite references in arguments of associations in annotation expressions
|
|
26
|
+
+ a references to a variable (`$user.id`, …) in a filter of an annotation expression
|
|
27
|
+
does not lead to a compiler message requesting users to provide the annotation themselves
|
|
28
|
+
+ improve code completion in annotation expressions: the editor can display valid names
|
|
29
|
+
for references even if the expression does not properly end by `)`
|
|
30
|
+
- to.sql:
|
|
31
|
+
+ reject `$self` in infix filter following exists predicate instead of just ignoring the filter expression
|
|
32
|
+
+ properly add comparison for the `tenant` discriminator to the `join` condition of `localized` views
|
|
33
|
+
if the non-published option for tenant support is set (regression with v6.4.0)
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
10
36
|
|
|
11
37
|
## Version 6.4.2 - 2025-10-07
|
|
12
38
|
|
|
@@ -17,8 +43,8 @@ The compiler behavior concerning `beta` features can change at any time without
|
|
|
17
43
|
+ avoid clutter in message text for syntax errors: use `‹Value›` instead of listing value tokens
|
|
18
44
|
- compiler: fix suppression of warnings when annotating backend-generated things
|
|
19
45
|
like draft entities or localized convenience views
|
|
20
|
-
- to.sql|hdi|hdbcds:
|
|
21
|
-
|
|
46
|
+
- to.sql|hdi|hdbcds: don’t report unjustified errors when projecting structured elements and
|
|
47
|
+
calculated elements had been used (regression with v6.4.0)
|
|
22
48
|
|
|
23
49
|
## Version 6.4.0 - 2025-09-26
|
|
24
50
|
|
|
@@ -239,6 +265,19 @@ The compiler behavior concerning `beta` features can change at any time without
|
|
|
239
265
|
|
|
240
266
|
- for.odata/to.edm(x):
|
|
241
267
|
+ Annotating the generated `DraftAdministrativeData` artifacts and their elements is now supported.
|
|
268
|
+
- beta flag `v7preview`: if set, the compiler reports those issues as errors
|
|
269
|
+
which we consider severe enough to report as error with the v7 release.
|
|
270
|
+
- new deprecated flags:
|
|
271
|
+
+ If the deprecated flag `noQuasiVirtualAssocs` is set, managed to-many associations
|
|
272
|
+
will get foreign keys as they got in compiler v5. If not set, managed to-many associations
|
|
273
|
+
without explicit foreign keys don't get `keys` anymore in cds-compiler v6.
|
|
274
|
+
+ If the deprecated flag `noCompositionIncludes` is set, generated entities for compositions
|
|
275
|
+
of named aspect will not get an `includes` property.
|
|
276
|
+
+ If the deprecated flag `noPersistenceJournalForGeneratedEntities` is set,
|
|
277
|
+
`@cds.persistence.journal` will _not_ be propagated to generated entities,
|
|
278
|
+
including generated `.texts` entities for localized entities, nor generated entities
|
|
279
|
+
for managed compositions of aspects. If not set, this annotation is copied to those entities
|
|
280
|
+
in compiler v6.
|
|
242
281
|
|
|
243
282
|
### Removed
|
|
244
283
|
|
|
@@ -247,6 +286,9 @@ The compiler behavior concerning `beta` features can change at any time without
|
|
|
247
286
|
+ v5 deprecated flags are removed, see [CHANGELOG_DEPRECATED.md](doc/CHANGELOG_DEPRECATED.md).
|
|
248
287
|
+ Option `compositionIncludes` is removed, as its default is `true`; instead, a deprecated flag was added.
|
|
249
288
|
- to.hdbcds: The HDBCDS backend is deprecated and can no longer be invoked.
|
|
289
|
+
- beta feature `v6preview`
|
|
290
|
+
- deprecated flags `includesNonShadowedFirst`, `eagerPersistenceForGeneratedEntities` and
|
|
291
|
+
noKeyPropagationWithExpansions`
|
|
250
292
|
|
|
251
293
|
### Fixed
|
|
252
294
|
|
|
@@ -358,6 +400,8 @@ The compiler behavior concerning `beta` features can change at any time without
|
|
|
358
400
|
in parentheses such as `[ (1), (2) ]`, as well as "infinite" by using `[ _, _ ]`.
|
|
359
401
|
- for.odata/to.edm(x)/for.seal: Propagate annotation expressions from managed associations
|
|
360
402
|
to the foreign keys
|
|
403
|
+
- beta feature `v6preview`: if set, the compiler reports those issues as errors
|
|
404
|
+
which we consider severe enough to report as error with the v6 release.
|
|
361
405
|
|
|
362
406
|
### Changed
|
|
363
407
|
|
|
@@ -530,6 +574,11 @@ The compiler behavior concerning `beta` features can change at any time without
|
|
|
530
574
|
- CDL parser: Issue warning for arrayed parameter with default value.
|
|
531
575
|
- to.cdl: Arrayed parameters with default values were not rendered correctly.
|
|
532
576
|
|
|
577
|
+
### Removed
|
|
578
|
+
|
|
579
|
+
- beta flag `optionalActionFunctionParameters`: in v5, action and function parameters
|
|
580
|
+
can be specified as optional without setting this beta flag.
|
|
581
|
+
|
|
533
582
|
## Version 5.2.0 - 2024-08-27
|
|
534
583
|
|
|
535
584
|
### Added
|
|
@@ -645,6 +694,7 @@ This is a preview version for the major release and contains breaking changes. I
|
|
|
645
694
|
|
|
646
695
|
- API: Deprecated functions `preparedCsnToEdmx` and `preparedCsnToEdm` were removed.
|
|
647
696
|
Use `to.edm(x)` instead.
|
|
697
|
+
- beta feature `v5preview`
|
|
648
698
|
|
|
649
699
|
## Version 4.9.10 - 2025-04-29
|
|
650
700
|
|
|
@@ -707,8 +707,8 @@ const centralMessageTexts = {
|
|
|
707
707
|
on: 'Unexpected $(ID) reference; is valid only if compared to be equal to an association of the target side',
|
|
708
708
|
subQuery: 'Unexpected $(ID) reference in a sub query',
|
|
709
709
|
setQuery: 'Unexpected $(ID) reference in a query on the right side of $(OP)',
|
|
710
|
-
exists: '
|
|
711
|
-
'exists-filter': 'Unexpected $(ID) reference in filter of
|
|
710
|
+
exists: 'Paths following $(NAME) must not start with $(ID)',
|
|
711
|
+
'exists-filter': 'Unexpected $(ID) reference in filter of assoc $(ELEMREF) following “EXISTS” predicate',
|
|
712
712
|
},
|
|
713
713
|
'ref-unexpected-map': {
|
|
714
714
|
std: 'Unexpected reference to an element of type $(TYPE)', // unused
|
|
@@ -3,39 +3,45 @@
|
|
|
3
3
|
const { requireForeignKeyAccess } = require('../checks/onConditions');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Filter expressions in an exists path must:
|
|
7
|
+
* - only contain fk-accesses for assocs. Unmanaged traversal / non-fk access is forbidden.
|
|
8
|
+
* - not contain a ref starting with $self
|
|
7
9
|
*
|
|
8
10
|
* @param {CSN.Artifact} parent
|
|
9
11
|
* @param {string} name
|
|
10
12
|
* @param {Array} expr
|
|
11
13
|
*/
|
|
12
|
-
function
|
|
14
|
+
function assertFilterOfExists( parent, name, expr ) {
|
|
13
15
|
for (let i = 0; i < expr.length - 1; i++) {
|
|
14
16
|
if (expr[i] === 'exists' && expr[i + 1].ref) {
|
|
15
17
|
i++;
|
|
16
18
|
const current = expr[i];
|
|
17
19
|
|
|
18
|
-
const { _links } =
|
|
20
|
+
const { _links } = current;
|
|
19
21
|
|
|
20
22
|
const assocs = _links.filter(link => link.art?.target).map(link => current.ref[link.idx]);
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
ensureValidFilters.call(this, assocs);
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
|
-
*
|
|
30
|
+
* Reject:
|
|
31
|
+
* - Unmanaged traversal / non-fk access.
|
|
32
|
+
* - ref's starting with $self
|
|
29
33
|
*
|
|
30
34
|
* @param {object[]} assocs Array of refs of assocs - possibly with a .where to check
|
|
31
35
|
*/
|
|
32
|
-
function
|
|
36
|
+
function ensureValidFilters( assocs ) {
|
|
33
37
|
for (const assoc of assocs) {
|
|
34
38
|
if (assoc.where) {
|
|
35
39
|
for (let i = 0; i < assoc.where.length; i++) {
|
|
36
40
|
const part = assoc.where[i];
|
|
37
41
|
|
|
38
42
|
if (part._links && !(assoc.where[i - 1] && assoc.where[i - 1] === 'exists')) {
|
|
43
|
+
if (part.$scope === '$self')
|
|
44
|
+
this.error('ref-unexpected-self', part.$path, { '#': 'exists-filter', elemref: assoc.id, id: part.ref[0] });
|
|
39
45
|
for (const link of part._links) {
|
|
40
46
|
if (link.art && link.art.target) {
|
|
41
47
|
if (link.art.keys) { // managed - allow FK access
|
|
@@ -55,7 +61,7 @@ function checkForInvalidAssoc( assocs ) {
|
|
|
55
61
|
}
|
|
56
62
|
// Recursively drill down if the assoc-step has a filter
|
|
57
63
|
if (part.ref[link.idx].where)
|
|
58
|
-
|
|
64
|
+
ensureValidFilters.call(this, [ part.ref[link.idx] ]);
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
67
|
}
|
|
@@ -65,7 +71,7 @@ function checkForInvalidAssoc( assocs ) {
|
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
module.exports = {
|
|
68
|
-
having:
|
|
69
|
-
where:
|
|
70
|
-
xpr:
|
|
74
|
+
having: assertFilterOfExists,
|
|
75
|
+
where: assertFilterOfExists,
|
|
76
|
+
xpr: assertFilterOfExists,
|
|
71
77
|
};
|
|
@@ -13,8 +13,8 @@ function existsMustEndInAssoc( parent, prop, expression, path ) {
|
|
|
13
13
|
if (expression[i] === 'exists') {
|
|
14
14
|
const next = expression[i + 1];
|
|
15
15
|
const { _art } = next;
|
|
16
|
-
const errorPath = path.concat([ prop, i ]);
|
|
17
16
|
if (!next.SELECT && !_art?.target) {
|
|
17
|
+
const errorPath = path.concat([ prop, i ]);
|
|
18
18
|
this.error('ref-expecting-assoc', errorPath, {
|
|
19
19
|
'#': _art.type ? 'with-type' : 'std',
|
|
20
20
|
elemref: next,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A path following an “exists” predicate must always end in an association.
|
|
5
|
+
*
|
|
6
|
+
* @param {object} parent
|
|
7
|
+
* @param {string} prop
|
|
8
|
+
* @param {Array} expression
|
|
9
|
+
* @param {CSN.Path} path
|
|
10
|
+
*/
|
|
11
|
+
function existsMustNotStartWithDollarSelf( parent, prop, expression, path ) {
|
|
12
|
+
for (let i = 0; i < expression?.length - 1; i++) {
|
|
13
|
+
if (expression[i] === 'exists') {
|
|
14
|
+
const next = expression[i + 1];
|
|
15
|
+
if (next.$scope === '$self') {
|
|
16
|
+
const errorPath = path.concat([ prop, i ]);
|
|
17
|
+
this.error('ref-unexpected-self', errorPath, {
|
|
18
|
+
'#': 'exists',
|
|
19
|
+
id: next.ref[0],
|
|
20
|
+
name: 'exists',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
having: existsMustNotStartWithDollarSelf,
|
|
29
|
+
where: existsMustNotStartWithDollarSelf,
|
|
30
|
+
xpr: existsMustNotStartWithDollarSelf,
|
|
31
|
+
};
|
package/lib/checks/validator.js
CHANGED
|
@@ -43,7 +43,8 @@ const { validateAssociationsInItems } = require('./arrayOfs');
|
|
|
43
43
|
const checkQueryForNoDBArtifacts = require('./queryNoDbArtifacts');
|
|
44
44
|
const checkExplicitlyNullableKeys = require('./nullableKeys');
|
|
45
45
|
const existsMustEndInAssoc = require('./existsMustEndInAssoc');
|
|
46
|
-
const
|
|
46
|
+
const existsMustNotStartWithDollarSelf = require('./existsMustNotStartWithDollarSelf');
|
|
47
|
+
const assertFilterOfExists = require('./existsExpressionsOnlyForeignKeys');
|
|
47
48
|
const checkPathsInStoredCalcElement = require('./checkPathsInStoredCalcElement');
|
|
48
49
|
const managedWithoutKeys = require('./managedWithoutKeys');
|
|
49
50
|
const {
|
|
@@ -85,7 +86,8 @@ const forRelationalDBArtifactValidators = [
|
|
|
85
86
|
const forRelationalDBCsnValidators = [
|
|
86
87
|
checkCdsMap,
|
|
87
88
|
existsMustEndInAssoc,
|
|
88
|
-
|
|
89
|
+
existsMustNotStartWithDollarSelf,
|
|
90
|
+
assertFilterOfExists,
|
|
89
91
|
navigationIntoMany,
|
|
90
92
|
checkPathsInStoredCalcElement,
|
|
91
93
|
featureFlags,
|
|
@@ -207,7 +207,7 @@ function assertConsistency( model, stage ) {
|
|
|
207
207
|
'elements', '$autoElement', '$uncheckedElements', '_origin', '_extensions',
|
|
208
208
|
'$requireElementAccess', '_effectiveType', '$effectiveSeqNo', '_deps',
|
|
209
209
|
'$calcDepElement', '$filtered', '$enclosed', '_parent',
|
|
210
|
-
'deprecated', '$
|
|
210
|
+
'deprecated', '$restricted',
|
|
211
211
|
],
|
|
212
212
|
schema: {
|
|
213
213
|
kind: { test: isString, enum: [ 'builtin' ] },
|
|
@@ -216,7 +216,7 @@ function assertConsistency( model, stage ) {
|
|
|
216
216
|
$uncheckedElements: { test: isBoolean },
|
|
217
217
|
$requireElementAccess: { test: isBoolean },
|
|
218
218
|
deprecated: { test: isBoolean },
|
|
219
|
-
$
|
|
219
|
+
$restricted: { test: TODO },
|
|
220
220
|
// missing location for normal "elements"
|
|
221
221
|
elements: { test: TODO },
|
|
222
222
|
},
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -211,8 +211,7 @@ const magicVariables = {
|
|
|
211
211
|
},
|
|
212
212
|
// Require that elements are accessed, i.e. no $draft, only $draft.<element>.
|
|
213
213
|
$requireElementAccess: true,
|
|
214
|
-
//
|
|
215
|
-
$onlyInExprCtx: [ 'annotation', 'annoRewrite' ],
|
|
214
|
+
$restricted: true, // only in annotation expression, see shared.js
|
|
216
215
|
},
|
|
217
216
|
};
|
|
218
217
|
|
|
@@ -454,8 +453,8 @@ function initBuiltins( model ) {
|
|
|
454
453
|
art.$requireElementAccess = magic.$requireElementAccess;
|
|
455
454
|
if (magic.deprecated)
|
|
456
455
|
art.deprecated = magic.deprecated;
|
|
457
|
-
if (magic.$
|
|
458
|
-
art.$
|
|
456
|
+
if (magic.$restricted)
|
|
457
|
+
art.$restricted = magic.$restricted;
|
|
459
458
|
|
|
460
459
|
createMagicElements( art, magic.elements );
|
|
461
460
|
if (options.variableReplacements?.[id])
|
|
@@ -480,8 +479,8 @@ function initBuiltins( model ) {
|
|
|
480
479
|
// Propagate this property so that it is available for sub-elements.
|
|
481
480
|
if (art.$uncheckedElements)
|
|
482
481
|
magic.$uncheckedElements = art.$uncheckedElements;
|
|
483
|
-
if (art.$
|
|
484
|
-
magic.$
|
|
482
|
+
if (art.$restricted)
|
|
483
|
+
magic.$restricted = art.$restricted;
|
|
485
484
|
setProp( magic, '_parent', art );
|
|
486
485
|
// setProp( magic, '_effectiveType', magic );
|
|
487
486
|
if (elements[id] && typeof elements[id] === 'object')
|