@sap/cds-compiler 6.7.1 → 6.7.3

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
@@ -13,6 +13,22 @@ we might not list every change in its behavior here.
13
13
  Productive code should never require a `beta` flag to be set, and
14
14
  might use a deprecated flag only for a limited period of time.
15
15
 
16
+ ## Version 6.7.3 - 2026-02-11
17
+
18
+ ### Fixed
19
+
20
+ - **sql:** do not resolve path navigations to virtual elements which resulted in an internal error.
21
+
22
+ ## Version 6.7.2 - 2026-02-04
23
+
24
+ ### Fixed
25
+
26
+ - **compiler:** Just issue warning for `using` declaration referring to nothing
27
+ (fixes regression introduced with v6.7.0 if there is a file containing
28
+ a `namespace` declaration, but no definitions)
29
+ - **effective:** clean up internal Symbols from meta section
30
+ - **sql:** clean up internal Symbols from meta section
31
+
16
32
  ## Version 6.7.1 - 2026-01-28
17
33
 
18
34
  ### Fixed
@@ -20,7 +36,6 @@ might use a deprecated flag only for a limited period of time.
20
36
  - compiler: Properly accept aspects as composition targets in an `extend`
21
37
  (fixes regression introduced with v6.7.0)
22
38
 
23
-
24
39
  ## Version 6.7.0 - 2026-01-23
25
40
 
26
41
  ### Added
@@ -160,10 +160,12 @@ const centralMessages = {
160
160
  'type-ambiguous-target': { severity: 'Warning' },
161
161
 
162
162
  'ref-unexpected-autoexposed': { severity: 'Error' },
163
+ 'ref-unexpected-many-expand': { severity: 'Error' },
163
164
  'ref-unexpected-many-navigation': { severity: 'Error' },
164
165
  // Published! Used in @sap/cds-lsp; if renamed, add to oldMessageIds and contact colleagues
165
166
  'ref-undefined-art': { severity: 'Error' },
166
167
  'ref-undefined-def': { severity: 'Error' },
168
+ 'ref-undefined-using': { severity: 'Warning' },
167
169
  'ref-undefined-var': { severity: 'Error' },
168
170
  'ref-undefined-element': { severity: 'Error' },
169
171
  'anno-undefined-element': { severity: 'Error' },
@@ -738,6 +740,7 @@ const centralMessageTexts = {
738
740
  std: 'No artifact has been found with name $(ART)',
739
741
  localized: 'Can\'t extend localized definitions, only annotate them using an $(KEYWORD) statement',
740
742
  },
743
+ 'ref-undefined-using': 'There is no definition in the model whose name starts with $(ART)',
741
744
  // TODO: proposal 'No definition found for $(NAME)',
742
745
  'ref-undefined-element': {
743
746
  std: 'Element $(ART) has not been found',
@@ -786,6 +789,10 @@ const centralMessageTexts = {
786
789
  std: '$(ART) can\'t be extended because it originates from an include',
787
790
  elements: '$(ART) can\'t be extended by elements/enums because it originates from an include',
788
791
  },
792
+ 'ref-unexpected-many-expand': {
793
+ std: 'Unexpected expand with to-many association',
794
+ composition: 'Unexpected expand with to-many composition',
795
+ },
789
796
  'ref-unexpected-many-navigation': {
790
797
  std: 'Unexpected navigation into arrayed structure',
791
798
  },
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { applyTransformationsOnNonDictionary } = require('../model/csnUtils');
4
+
5
+ /**
6
+ * Check for nested projections (expand) on to-many associations or compositions.
7
+ * Nested projections are only valid for to-one relationships.
8
+ *
9
+ * @param {object} parent Object with the expression as a property
10
+ * @param {string} propOnParent Name of the expression property on parent
11
+ * @param {Array} _e Expression to check (unused)
12
+ * @param {CSN.Path} path
13
+ */
14
+ function expandToMany( parent, propOnParent, _e, path ) {
15
+ applyTransformationsOnNonDictionary(parent, propOnParent, {
16
+ expand: (_parent, _prop, _expand, _path) => {
17
+ const last = _parent._links?.at(-1).art;
18
+ if (last?.cardinality && last.cardinality.max !== 1) {
19
+ this.message('ref-unexpected-many-expand', _path,
20
+ { '#': last.type === 'cds.Composition' ? 'composition' : 'std' });
21
+ }
22
+ },
23
+ }, null, path);
24
+ }
25
+
26
+ module.exports = {
27
+ columns: expandToMany,
28
+ };
@@ -12,6 +12,7 @@ const { validateSelectItems } = require('./selectItems');
12
12
  const { rejectParamDefaultsInHanaCds, warnAboutDefaultOnAssociationForHanaCds } = require('./defaultValues');
13
13
  const validateCdsPersistenceAnnotation = require('./cdsPersistence');
14
14
  const navigationIntoMany = require('./manyNavigations');
15
+ const expandToMany = require('./manyExpand');
15
16
  const checkUsedTypesForAnonymousAspectComposition = require('./managedInType');
16
17
  const validateHasPersistedElements = require('./hasPersistedElements');
17
18
  const checkForHanaTypes = require('./checkForTypes');
@@ -91,6 +92,7 @@ const forRelationalDBCsnValidators = [
91
92
  existsMustNotStartWithDollarSelf,
92
93
  assertFilterOfExists,
93
94
  navigationIntoMany,
95
+ expandToMany,
94
96
  checkPathsInStoredCalcElement,
95
97
  featureFlags,
96
98
  ];
@@ -43,11 +43,15 @@ function fns( model ) {
43
43
  // Map `exprCtx` (is a param of traversal functions) to reference semantics
44
44
  const referenceSemantics = {
45
45
  // global: ------------------------------------------------------------------
46
- using: { // only used to produce error message
46
+ using: { // only used to produce warnings
47
47
  isMainRef: 'no-leaf-gap',
48
48
  lexical: null,
49
49
  dynamic: modelDefinitions,
50
50
  notFound: undefinedDefinition,
51
+ messageMap: {
52
+ 'ref-undefined-art': 'ref-undefined-using',
53
+ 'ref-undefined-def': 'ref-undefined-using',
54
+ },
51
55
  },
52
56
  // scope:'global': for cds.Association and auto-redirected targets
53
57
  $global: {
@@ -49,7 +49,7 @@ function addToSetAttr( carrier, propName, propValue, removeFromType = true ) {
49
49
 
50
50
  function applyAppSpecificLateCsnTransformationOnElement( options, element, struct, error ) {
51
51
  if (options.isV2() && struct['@Aggregation.ApplySupported.PropertyRestrictions'])
52
- mapAnnotationAssignment(element, struct, AnalyticalAnnotations());
52
+ mapAnnotationAssignment(element, struct, GenerateAnalyticalAnnotations());
53
53
 
54
54
 
55
55
  // etag requires Core.OptimisticConcurrency to be set in V4 (cap/issues#2641)
@@ -66,7 +66,7 @@ function applyAppSpecificLateCsnTransformationOnElement( options, element, struc
66
66
  (struct['@Core.OptimisticConcurrency'] || [])/* .push(element.name) */);
67
67
  }
68
68
 
69
- function AnalyticalAnnotations() {
69
+ function GenerateAnalyticalAnnotations() {
70
70
  function mapCommonAttributes( elt, structure, prop ) {
71
71
  const CommonAttributes = elt[prop];
72
72
  if (!Array.isArray(CommonAttributes)) {
@@ -104,7 +104,7 @@ class SqlRenderEnvironment {
104
104
  * }
105
105
  * }
106
106
  *
107
- * @param {CSN.Model} csn HANA transformed CSN
107
+ * @param {CSN.Model} csn for SQL transformed CSN
108
108
  * @param {CSN.Options} options Transformation options
109
109
  * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
110
110
  * @returns {object} Dictionary of artifact-type:artifacts, where artifacts is a dictionary of name:content
@@ -2,14 +2,12 @@
2
2
 
3
3
  const {
4
4
  applyTransformations,
5
- setDependencies,
6
5
  walkCsnPath,
7
6
  getUtils,
8
7
  forEachDefinition,
9
8
  } = require('../../model/csnUtils');
10
9
  const { implicitAs, columnAlias, pathId } = require('../../model/csnRefs');
11
10
  const { setProp } = require('../../base/model');
12
- const { forEach } = require('../../utils/objectUtils');
13
11
  const { killNonrequiredAnno } = require('./killAnnotations');
14
12
  const { featureFlags } = require('../featureFlags');
15
13
  const { applyTransformationsOnNonDictionary } = require('./applyTransformations');
@@ -128,15 +126,11 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
128
126
  * For such skipped things, error for usage of assoc pointing to them and ignore publishing of assoc pointing to them.
129
127
  */
130
128
  function rewriteExpandInline() {
131
- let cleanup = [];
132
- let _dependents;
133
-
134
129
  const entity = findAnEntity();
135
130
  const toDummify = [];
136
131
 
137
132
  applyTransformations(csn, {
138
- columns: (parent, name, columns, path) => {
139
- const artifact = csn.definitions[path[1]];
133
+ columns: (parent) => {
140
134
  // get$combined expects a SET/SELECT - so we wrap the parent
141
135
  // (which is the thing inside SET/SELECT)
142
136
  // We can directly use SELECT here, as only projections and SELECT can have .columns
@@ -146,18 +140,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
146
140
  root[key] = root[key][0].element;
147
141
  });
148
142
  const rewritten = rewrite(root, parent.columns, parent.excluding);
149
- /*
150
- * Do not remove unexpandable many columns in OData
151
- */
152
- if (rewritten.toMany.length > 0 && !options.toOdata) {
153
- markAsToDummify(artifact, path[1]);
154
- rewritten.toMany.forEach(({ art }) => {
155
- error( null, art.$path || [ 'definitions', path[1] ], { name: `${ art.$env || path[1] }:${ art.ref.map(r => r.id || r) }` }, 'Unexpected .expand with to-many association $(NAME)');
156
- });
157
- }
158
- else {
159
- parent.columns = rewritten.columns;
160
- }
143
+ parent.columns = rewritten.columns;
161
144
  },
162
145
  });
163
146
 
@@ -166,8 +149,6 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
166
149
  if (!options.toOdata)
167
150
  dummyfy();
168
151
 
169
- cleanup.forEach(fn => fn());
170
-
171
152
  csnUtils = getUtils(csn);
172
153
 
173
154
  const publishing = [];
@@ -267,29 +248,6 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
267
248
  info(null, path, { name: last, target }, 'Ignoring association $(NAME) with target $(TARGET), because it was skipped because of .expand in conjunction with to-many');
268
249
  }
269
250
 
270
- /**
271
- * Mark the given artifact and all (transitively) dependent artifacts as `toDummify`.
272
- * This means that they will be replaced with simple dummy views in `@dummify`
273
- *
274
- * @param {CSN.Artifact} artifact
275
- * @param {string} name
276
- */
277
- function markAsToDummify( artifact, name ) {
278
- if (!_dependents && cleanup.length === 0)
279
- ({ cleanup, _dependents } = setDependencies(csn, csnUtils));
280
-
281
- const stack = [ [ artifact, name ] ];
282
- while (stack.length > 0) {
283
- const [ a, n ] = stack.pop();
284
- if (a[_dependents]) {
285
- forEach(a[_dependents], (dependentName, dependent) => {
286
- stack.push([ dependent, dependentName ]);
287
- });
288
- }
289
- toDummify.push(n);
290
- }
291
- }
292
-
293
251
  /**
294
252
  * Replace the artifacts in `toDummify` with simple dummy views as produced by createDummyView.
295
253
  */
@@ -17,7 +17,7 @@ const misc = require('./misc');
17
17
  const annotations = require('./annotations');
18
18
  const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities } = require('../db/rewriteCalculatedElements');
19
19
  const { cloneFullCsn } = require('../../model/cloneCsn');
20
- const { featureFlags } = require('../featureFlags');
20
+ const { featureFlags, removeFeatureFlags } = require('../featureFlags');
21
21
  const getServiceFilterFunction = require('./service');
22
22
  const { traverseQuery } = require('../../model/csnRefs');
23
23
  const { expandWildcard } = require('../db/expansion');
@@ -136,6 +136,7 @@ function effectiveCsn( model, options, messageFunctions ) {
136
136
  }
137
137
 
138
138
  messageFunctions.throwWithError();
139
+ removeFeatureFlags(csn);
139
140
 
140
141
  return csn;
141
142
  }
@@ -2,4 +2,9 @@
2
2
 
3
3
  const featureFlags = Symbol.for('Feature flags');
4
4
 
5
- module.exports = { featureFlags };
5
+ function removeFeatureFlags(csn) {
6
+ if (csn.meta)
7
+ delete csn.meta[featureFlags];
8
+ }
9
+
10
+ module.exports = { featureFlags, removeFeatureFlags };
@@ -34,7 +34,7 @@ const temporal = require('./db/temporal');
34
34
  const associations = require('./db/associations');
35
35
  const backlinks = require('./db/backlinks');
36
36
  const { getDefaultTypeLengths } = require('../render/utils/common');
37
- const { featureFlags } = require('./featureFlags');
37
+ const { featureFlags, removeFeatureFlags } = require('./featureFlags');
38
38
  const { cloneCsnNonDict, cloneFullCsn } = require('../model/cloneCsn');
39
39
  const { processSqlServices, createServiceDummy } = require('./db/processSqlServices');
40
40
  const { expandWildcard } = require('./db/expansion');
@@ -465,6 +465,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
465
465
 
466
466
  killTypes.forEach(fn => fn());
467
467
  redoProjections.forEach(fn => fn());
468
+ removeFeatureFlags(csn);
468
469
 
469
470
  timetrace.stop('Transform CSN');
470
471
  timetrace.stop('HANA transformation');
@@ -331,10 +331,16 @@ function translateAssocsToJoins(model, inputOptions = {}) {
331
331
  pos++;
332
332
  // QA + tail is the rewritten path
333
333
  tail = tail.slice(pos);
334
+
335
+ // leave virtual paths as is
336
+ if ( tail.at(-1)._artifact.virtual?.val ) {
337
+ replaceNodeContent(pathNode, constructPathNode([ constructTableAliasPathStep(QA), ...tail ]));
338
+ return;
339
+ }
340
+
334
341
  // check from left to right (longest match) if a subsequent QAT is $njr
335
342
  // if so, substitute path with pregenerated foreign key, prepend by optional
336
343
  // (to be flattened) prefix
337
-
338
344
  for (let i = 0; i < tail.length - 1; i++) {
339
345
  if (tail[i]._navigation) {
340
346
  // the correct flattened foreign key must match the leaf artifact and access path prefix of this path
@@ -345,36 +351,30 @@ function translateAssocsToJoins(model, inputOptions = {}) {
345
351
  throw new CompilerAssertion('Debug me: No FK found for FK rewriting');
346
352
  }
347
353
  }
348
- // leave virtual paths as is
349
- if ( tail.at(-1)._artifact.virtual?.val === true ) {
350
- replaceNodeContent(pathNode, constructPathNode([ constructTableAliasPathStep(QA), ...tail ]));
351
- }
352
- else {
353
- const newTail = [];
354
- // construct the tail (segment after last join relevant navigation) and preserve association steps
355
- // each association may have a structured prefix
356
- // `struct.foo.bar.assoc. otherStruct.baz.otherAssoc.fk`
357
- // --> `[otherStruct_baz_otherAssoc, fk]
358
- for (let i = 0; i < tail.length; i++) {
359
- if (tail[i]._navigation) {
360
- const nextNavigation = tail.slice(i + 1).findIndex(ps => ps._navigation);
361
- if (nextNavigation >= 0) {
362
- newTail.push({
363
- id: tail.slice(i, i + 1 + nextNavigation).map(ps => ps.id).join(pathDelimiter),
364
- _artifact: tail[i + nextNavigation]._artifact,
365
- });
366
- }
367
- else {
368
- newTail.push({
369
- id: tail.slice(i).map(ps => ps.id).join(pathDelimiter),
370
- _artifact: tail.at(-1)._artifact,
371
- });
372
- }
354
+ const newTail = [];
355
+ // construct the tail (segment after last join relevant navigation) and preserve association steps
356
+ // each association may have a structured prefix
357
+ // `struct.foo.bar.assoc. otherStruct.baz.otherAssoc.fk`
358
+ // --> `[otherStruct_baz_otherAssoc, fk]
359
+ for (let i = 0; i < tail.length; i++) {
360
+ if (tail[i]._navigation) {
361
+ const nextNavigation = tail.slice(i + 1).findIndex(ps => ps._navigation);
362
+ if (nextNavigation >= 0) {
363
+ newTail.push({
364
+ id: tail.slice(i, i + 1 + nextNavigation).map(ps => ps.id).join(pathDelimiter),
365
+ _artifact: tail[i + nextNavigation]._artifact,
366
+ });
367
+ }
368
+ else {
369
+ newTail.push({
370
+ id: tail.slice(i).map(ps => ps.id).join(pathDelimiter),
371
+ _artifact: tail.at(-1)._artifact,
372
+ });
373
373
  }
374
374
  }
375
- replaceNodeContent(pathNode,
376
- constructPathNode([ constructTableAliasPathStep(QA), ...newTail ]));
377
375
  }
376
+ replaceNodeContent(pathNode,
377
+ constructPathNode([ constructTableAliasPathStep(QA), ...newTail ]));
378
378
  }
379
379
 
380
380
  function findForeignKey(assoc, fk) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "6.7.1",
3
+ "version": "6.7.3",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",