@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 CHANGED
@@ -4,9 +4,35 @@
4
4
  <!-- markdownlint-disable MD004 -->
5
5
  <!-- (no-duplicate-heading)-->
6
6
 
7
- Note: `beta` fixes, changes and features are usually not listed in this ChangeLog
8
- but in [doc/CHANGELOG_BETA.md](doc/CHANGELOG_BETA.md).
9
- The compiler behavior concerning `beta` features can change at any time without notice.
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: Fix handling of structured columns when calculated elements are used
21
- don't add explicit casts too eagerly
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: 'With $(NAME), path steps must not start with $(ID)',
711
- 'exists-filter': 'Unexpected $(ID) reference in filter of path $(ELEMREF) following “EXISTS” predicate',
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
- * Check that associations in filters (in an exists expression) are only fk-accesses. Everything else is forbidden.
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 forbidAssocInExists( parent, name, expr ) {
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 } = expr[i];
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
- checkForInvalidAssoc.call(this, assocs);
24
+ ensureValidFilters.call(this, assocs);
23
25
  }
24
26
  }
25
27
  }
26
28
 
27
29
  /**
28
- * Check that associations in filters (in an exists expression) are only fk-accesses. Everything else is forbidden.
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 checkForInvalidAssoc( assocs ) {
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
- checkForInvalidAssoc.call(this, [ part.ref[link.idx] ]);
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: forbidAssocInExists,
69
- where: forbidAssocInExists,
70
- xpr: forbidAssocInExists,
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
+ };
@@ -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 forbidAssocInExists = require('./existsExpressionsOnlyForeignKeys');
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
- forbidAssocInExists,
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', '$onlyInExprCtx',
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
- $onlyInExprCtx: { test: TODO },
219
+ $restricted: { test: TODO },
220
220
  // missing location for normal "elements"
221
221
  elements: { test: TODO },
222
222
  },
@@ -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
- // See reference semantics in shared.js
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.$onlyInExprCtx)
458
- art.$onlyInExprCtx = magic.$onlyInExprCtx;
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.$onlyInExprCtx)
484
- magic.$onlyInExprCtx = art.$onlyInExprCtx;
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')