@sap/cds-compiler 5.1.2 → 5.3.0

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/bin/cdsc.js +7 -2
  3. package/bin/cdshi.js +24 -17
  4. package/bin/cdsse.js +17 -18
  5. package/doc/CHANGELOG_BETA.md +9 -4
  6. package/lib/api/main.js +19 -2
  7. package/lib/api/options.js +4 -1
  8. package/lib/api/validate.js +5 -0
  9. package/lib/base/builtins.js +1 -0
  10. package/lib/base/message-registry.js +40 -3
  11. package/lib/base/messages.js +1 -1
  12. package/lib/base/model.js +0 -11
  13. package/lib/checks/actionsFunctions.js +0 -12
  14. package/lib/checks/structuredAnnoExpressions.js +10 -14
  15. package/lib/compiler/assert-consistency.js +21 -13
  16. package/lib/compiler/builtins.js +2 -2
  17. package/lib/compiler/checks.js +25 -6
  18. package/lib/compiler/define.js +27 -31
  19. package/lib/compiler/extend.js +16 -18
  20. package/lib/compiler/generate.js +3 -3
  21. package/lib/compiler/populate.js +22 -16
  22. package/lib/compiler/propagator.js +3 -2
  23. package/lib/compiler/resolve.js +87 -94
  24. package/lib/compiler/shared.js +12 -13
  25. package/lib/compiler/tweak-assocs.js +390 -86
  26. package/lib/compiler/utils.js +41 -33
  27. package/lib/compiler/xpr-rewrite.js +45 -58
  28. package/lib/edm/annotations/genericTranslation.js +17 -13
  29. package/lib/edm/csn2edm.js +28 -4
  30. package/lib/edm/edm.js +68 -28
  31. package/lib/edm/edmInboundChecks.js +5 -8
  32. package/lib/edm/edmPreprocessor.js +66 -40
  33. package/lib/edm/edmUtils.js +1 -1
  34. package/lib/gen/BaseParser.js +778 -0
  35. package/lib/gen/CdlParser.js +4477 -0
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +1 -1
  38. package/lib/gen/languageParser.js +4072 -4024
  39. package/lib/inspect/inspectPropagation.js +1 -1
  40. package/lib/json/from-csn.js +5 -3
  41. package/lib/json/to-csn.js +7 -10
  42. package/lib/language/antlrParser.js +96 -0
  43. package/lib/language/errorStrategy.js +1 -1
  44. package/lib/language/genericAntlrParser.js +32 -4
  45. package/lib/language/multiLineStringParser.js +1 -1
  46. package/lib/main.d.ts +23 -0
  47. package/lib/model/cloneCsn.js +22 -13
  48. package/lib/model/csnUtils.js +2 -0
  49. package/lib/model/revealInternalProperties.js +2 -0
  50. package/lib/modelCompare/utils/filter.js +70 -42
  51. package/lib/optionProcessor.js +16 -10
  52. package/lib/parsers/AstBuildingParser.js +1290 -0
  53. package/lib/parsers/CdlGrammar.g4 +2013 -0
  54. package/lib/parsers/Lexer.js +249 -0
  55. package/lib/render/toCdl.js +46 -45
  56. package/lib/render/toSql.js +5 -5
  57. package/lib/transform/addTenantFields.js +4 -4
  58. package/lib/transform/db/applyTransformations.js +54 -16
  59. package/lib/transform/draft/odata.js +10 -11
  60. package/lib/transform/effective/flattening.js +10 -14
  61. package/lib/transform/forRelationalDB.js +7 -6
  62. package/lib/transform/odata/flattening.js +42 -31
  63. package/lib/transform/odata/toFinalBaseType.js +7 -6
  64. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  65. package/lib/utils/moduleResolve.js +1 -1
  66. package/package.json +2 -2
  67. package/share/messages/redirected-to-ambiguous.md +5 -4
  68. package/share/messages/redirected-to-complex.md +6 -3
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const {
4
- forEachDefinition, forEachMemberRecursively, findAnnotationExpression, applyTransformationsOnNonDictionary, transformExpression,
4
+ forEachDefinition, forEachMemberRecursively, applyTransformationsOnNonDictionary, transformExpression, transformAnnotationExpression,
5
5
  } = require('../../model/csnUtils');
6
6
  const { getStructStepsFlattener } = require('../db/flattening');
7
7
  const { setProp } = require('../../base/model');
@@ -15,7 +15,7 @@ const { forEach } = require('../../utils/objectUtils');
15
15
  * @param csnUtils
16
16
  * @param messageFunctions
17
17
  */
18
- function flattenRefs( csn, options, csnUtils, messageFunctions ) {
18
+ function flattenRefs(csn, options, csnUtils, messageFunctions) {
19
19
  const cleanup = [];
20
20
  forEachDefinition(csn, (artifact) => {
21
21
  if (artifact.elements) {
@@ -36,15 +36,11 @@ function flattenRefs( csn, options, csnUtils, messageFunctions ) {
36
36
 
37
37
  // Absolutify paths in annotation expressions
38
38
  Object.keys(element)
39
- .filter(pn => findAnnotationExpression(element, pn))
39
+ .filter(pn => pn.startsWith('@') && element[pn])
40
40
  .forEach((anno) => {
41
- applyTransformationsOnNonDictionary(element, anno, {
42
- xpr: (parent, prop) => {
43
- transformExpression(parent, prop, {
44
- ref: absolutifier,
45
- }, []);
46
- },
47
- }, {}, []);
41
+ transformAnnotationExpression(element, anno, {
42
+ ref: absolutifier,
43
+ }, []);
48
44
  if (element[anno].ref)
49
45
  absolutifier(element[anno], 'ref', element[anno].ref);
50
46
  });
@@ -83,17 +79,17 @@ function flattenRefs( csn, options, csnUtils, messageFunctions ) {
83
79
  };
84
80
 
85
81
  Object.keys(a)
86
- .filter(pn => findAnnotationExpression(a, pn))
82
+ .filter(pn => pn.startsWith('@') && a[pn])
87
83
  .forEach((pn) => {
88
- transformExpression(a, pn, [ markBindingParam, refFlattener ], [ 'definitions', defName, 'actions', an ]);
84
+ transformAnnotationExpression(a, pn, [ markBindingParam, refFlattener ], [ 'definitions', defName, 'actions', an ]);
89
85
  adaptRefs.forEach(fn => fn(true, 1, parent => parent.$bparam));
90
86
  adaptRefs.length = 0;
91
87
  });
92
88
 
93
89
 
94
90
  forEachMemberRecursively(a, (member, memberName, prop, path) => {
95
- Object.keys(member).filter(pn => findAnnotationExpression(member, pn)).forEach((pn) => {
96
- transformExpression(member, pn, [ markBindingParam, refFlattener ], path);
91
+ Object.keys(member).filter(pn => pn.startsWith('@') && member[pn]).forEach((pn) => {
92
+ transformAnnotationExpression(member, pn, [ markBindingParam, refFlattener ], path);
97
93
  adaptRefs.forEach(fn => fn(true, 1, parent => parent.$bparam));
98
94
  adaptRefs.length = 0;
99
95
  });
@@ -817,16 +817,17 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
817
817
  function checkTypeParameters(artifact, artifactName) {
818
818
  forEachMemberRecursively(artifact, (member, memberName, prop, path) => {
819
819
  // Check type parameters (length, precision, scale ...)
820
- if (!member.$ignore && member.type)
821
- _check(member, memberName, csn, path);
822
-
823
- if (!member.$ignore && member.items && member.items.type)
824
- _check(member.items, memberName, csn, path.concat([ 'items' ]));
820
+ if (!member.$ignore) {
821
+ if (member.type)
822
+ _check(member, memberName, csn, path);
823
+ if (member.items?.type)
824
+ _check(member.items, memberName, csn, path.concat([ 'items' ]));
825
+ }
825
826
  }, [ 'definitions', artifactName ]);
826
827
 
827
828
  // Check that required actual parameters on 'node.type' are set, that their values are in the correct range etc.
828
829
  function _check(node, nodeName, model, path) {
829
- if (node.type) {
830
+ if (node.type && !node.virtual) {
830
831
  const absolute = node.type;
831
832
  switch (absolute) {
832
833
  case 'cds.String':
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { forEachDefinition,
4
4
  copyAnnotations, forEachMemberRecursively,
5
- transformExpression, findAnnotationExpression } = require('../../model/csnUtils');
5
+ transformExpression, transformAnnotationExpression } = require('../../model/csnUtils');
6
6
  const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
7
7
  const transformUtils = require('../transformUtils');
8
8
  const { setProp } = require('../../base/model');
@@ -54,7 +54,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
54
54
  if (flatElt.items) {
55
55
  // rewrite annotation paths inside items.elements
56
56
  forEachMemberRecursively(flatElt.items, (elt, _eltName, _prop, path) => {
57
- const exprAnnos = Object.keys(elt).filter(pn => findAnnotationExpression(elt, pn));
57
+ const exprAnnos = Object.keys(elt).filter(pn => pn[0] === '@');
58
58
  flattenAndPrefixExprPaths(elt, exprAnnos, elt.$path, path, 0, true);
59
59
  }, [ flatEltName ], true, { pathWithoutProp: true } );
60
60
  }
@@ -67,7 +67,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
67
67
  });
68
68
  // entity annotations
69
69
  const flatAnnos = Object.create(null);
70
- const annoNames = copyAnnotations(def, flatAnnos).filter(an => findAnnotationExpression(def, an));
70
+ const annoNames = copyAnnotations(def, flatAnnos).filter(pn => pn[0] === '@');
71
71
  flattenAndPrefixExprPaths(flatAnnos, annoNames, [ 'definitions', defName ], [ defName ], 0);
72
72
  setProp(def, '$flatAnnotations', flatAnnos);
73
73
  // explicit binding parameter of bound action
@@ -100,10 +100,10 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
100
100
  };
101
101
 
102
102
  const flatAnnos = Object.create(null);
103
- const annoNames = copyAnnotations(action, flatAnnos).filter(an => findAnnotationExpression(action, an));
103
+ const annoNames = copyAnnotations(action, flatAnnos).filter(pn => pn[0] === '@');
104
104
  annoNames.forEach((an) => {
105
105
  refCheck.anno = an;
106
- transformExpression(flatAnnos, an,
106
+ transformAnnotationExpression(flatAnnos, an,
107
107
  [ markBindingParam, refCheck, refFlattener ],
108
108
  [ 'definitions', defName, 'actions', actionName ]);
109
109
  adaptRefs.forEach(fn => fn(true, 1, (parent) => parent.$bparam));
@@ -112,10 +112,10 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
112
112
  setProp(action, '$flatAnnotations', flatAnnos);
113
113
 
114
114
  forEachMemberRecursively(action, (member, memberName, prop, path, _parent) => {
115
- const exprAnnos = Object.keys(member).filter(pn => findAnnotationExpression(member, pn));
115
+ const exprAnnos = Object.keys(member).filter(pn => pn[0] === '@');
116
116
  exprAnnos.forEach((pn) => {
117
117
  refCheck.anno = pn;
118
- transformExpression(member, pn, [ markBindingParam, refCheck, refFlattener ], path);
118
+ transformAnnotationExpression(member, pn, [ markBindingParam, refCheck, refFlattener ], path);
119
119
  adaptRefs.forEach(fn => fn(true, 1, (parent) => parent.$bparam));
120
120
  adaptRefs.length = 0;
121
121
  });
@@ -193,7 +193,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
193
193
  };
194
194
  // TODO: copy only those expression annotations that have no path into unreachable subtree, starting
195
195
  // of typePathRoot (which could be some completely different path)
196
- const exprAnnoNames = copyAnnotations(elt, flatElt, false, excludes).filter(pn => findAnnotationExpression(flatElt, pn));
196
+ const exprAnnoNames = copyAnnotations(elt, flatElt, false, excludes).filter(pn => pn[0] === '@');
197
197
  flattenAndPrefixExprPaths(flatElt, exprAnnoNames, elt.$path, rootPrefix, typeIdx);
198
198
  // Copy selected type properties
199
199
  ['key', 'virtual', 'masked', 'viaAll', 'localized'].forEach(p => {
@@ -217,7 +217,7 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
217
217
  const [ nonAnnoProps, exprAnnoProps ] = Object.keys(flatElt).reduce((acc, pn) => {
218
218
  if (pn[0] !== '@' && pn !== 'value')
219
219
  acc[0].push(pn);
220
- if (findAnnotationExpression(flatElt, pn) || pn === 'value')
220
+ else
221
221
  acc[1].push(pn);
222
222
  return acc;
223
223
  }, [[],[]]);
@@ -326,9 +326,8 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
326
326
  return `${head}`;
327
327
  })();
328
328
 
329
- let refChanged = false;
330
329
  const absolutifier = {
331
- ref : (parent, prop, xpr) => {
330
+ ref : (parent, prop, xpr, _path, _p, _ppn, ctx) => {
332
331
  const head = xpr[0].id || xpr[0];
333
332
  let isPrefixed = false;
334
333
  if(!isMagicVariable(head)) {
@@ -365,20 +364,27 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
365
364
  parent[prop].unshift('$self');
366
365
  }
367
366
  }
368
- if(isPrefixed)
369
- refChanged = isPrefixed;
367
+ if(isPrefixed && ctx?.annoExpr?.['=']) {
368
+ ctx.annoExpr['='] = true;
369
+ }
370
370
  }
371
371
  }
372
+
373
+ refFlattener.$fnArgs = [ refParentIsItems ];
372
374
  propNames.forEach(pn => {
373
- refChanged = false;
374
375
  refCheck.anno = pn;
375
- transformExpression(carrier, pn, [ refCheck, refFlattener ], csnPath);
376
- adaptRefs.forEach(fn =>
377
- { if( fn(refParentIsItems)) refChanged = true });
378
- adaptRefs.length = 0;
379
- transformExpression(carrier, pn, absolutifier, csnPath)
380
- if(refChanged && carrier[pn]['='])
381
- carrier[pn]['='] = true;
376
+ if(pn[0] === '@') {
377
+ transformAnnotationExpression(carrier, pn, [ refCheck, refFlattener ], csnPath);
378
+ adaptRefs.forEach(fn => fn(refParentIsItems));
379
+ adaptRefs.length = 0;
380
+ transformAnnotationExpression(carrier, pn, absolutifier, csnPath);
381
+ }
382
+ if(pn === 'value') {
383
+ transformExpression(carrier, pn, [ refCheck, refFlattener ], csnPath);
384
+ adaptRefs.forEach(fn => fn(refParentIsItems));
385
+ adaptRefs.length = 0;
386
+ transformExpression(carrier, pn, absolutifier, csnPath);
387
+ }
382
388
  });
383
389
  }
384
390
 
@@ -454,14 +460,10 @@ function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef,
454
460
  typeNames.forEach(tn => {
455
461
  forEachMemberRecursively(csn.definitions[tn], (member, memberName, prop, csnPath) => {
456
462
  Object.keys(member).filter(pn => pn[0] === '@').forEach(pn => {
457
- let refChanged = false;
458
463
  refCheck.anno = pn;
459
- transformExpression(member, pn, [ refCheck, refFlattener ], csnPath);
460
- adaptRefs.forEach(fn => {
461
- if (fn(true, 1)) refChanged = true });
464
+ transformAnnotationExpression(member, pn, [ refCheck, refFlattener ], csnPath);
465
+ adaptRefs.forEach(fn => fn(true, 1));
462
466
  adaptRefs.length = 0;
463
- if(refChanged && member[pn]['='])
464
- member[pn]['='] = true;
465
467
  });
466
468
  }, [ 'definitions', tn ]);
467
469
  })
@@ -488,8 +490,7 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
488
490
  }
489
491
  const adaptRefs = [];
490
492
  const transformer = {
491
- ref: (parent, prop, ref, path) => {
492
- let refChanged = false;
493
+ ref: (parent, _prop, ref, path, _p, _ppn, ctx) => {
493
494
  const { links, art, scope } = inspectRef(path);
494
495
  const resolvedLinkTypes = resolveLinkTypes(links);
495
496
  setProp(parent, '$path', [ ...path ]);
@@ -502,9 +503,10 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
502
503
  // full path into target, uncomment this line and
503
504
  // comment/remove setProp in expansion.js
504
505
  // setProp(parent, '$structRef', parent.ref);
505
- [ parent.ref, refChanged ] = flattenStructStepsInRef(ref,
506
+ const [ newRef, refChanged ] = flattenStructStepsInRef(ref,
506
507
  scopedPath, links, scope, resolvedLinkTypes,
507
508
  suspend, suspendPos, parent.$bparam);
509
+ parent.ref = newRef;
508
510
  resolved.set(parent, { links, art, scope });
509
511
  // Explicitly set implicit alias for things that are now flattened - but only in columns
510
512
  // TODO: Can this be done elegantly during expand phase already?
@@ -546,8 +548,17 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
546
548
  && typeof path[path.length - 1] === 'number';
547
549
  }
548
550
  };
551
+
549
552
  // adapt queries later
550
- adaptRefs.push(fn);
553
+ if(ctx?.annoExpr?.['=']) {
554
+ const annoExpr = ctx.annoExpr;
555
+ adaptRefs.push((...args) => {
556
+ if(fn(...args))
557
+ annoExpr['='] = true;
558
+ });
559
+ }
560
+ else
561
+ adaptRefs.push(fn);
551
562
  },
552
563
  }
553
564
 
@@ -2,11 +2,10 @@
2
2
 
3
3
  const { setProp, isBetaEnabled } = require('../../base/model');
4
4
  const {
5
- transformExpression,
5
+ transformAnnotationExpression,
6
6
  forEachDefinition,
7
7
  forEachGeneric,
8
8
  forEachMemberRecursively,
9
- findAnnotationExpression,
10
9
  } = require('../../model/csnUtils');
11
10
  const { isBuiltinType } = require('../../base/builtins');
12
11
  const { isArtifactInSomeService, isArtifactInService } = require('./utils');
@@ -224,9 +223,9 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
224
223
  const usingPositionStr = f($path2path(location));
225
224
  const eltRootPath = $path2path(elt.$path);
226
225
 
227
- Object.keys(elt).filter(pn => pn[0] === '@' && findAnnotationExpression(elt, pn)).forEach(xprAName => {
228
- transformExpression(elt, xprAName, {
229
- ref: (parent, prop, xpr, csnPath) => {
226
+ Object.keys(elt).filter(pn => pn[0] === '@').forEach(anno => {
227
+ transformAnnotationExpression(elt, anno, {
228
+ ref: (parent, prop, xpr, csnPath, _p, _ppn, ctx) => {
230
229
  let prefixMatch = true;
231
230
  const head = xpr[0].id || xpr[0];
232
231
  if (head === '$self') {
@@ -238,10 +237,12 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
238
237
  parent[prop] = [ ...xpr.slice(eltRootPath.length-1)];
239
238
  else
240
239
  parent[prop] = [ '$self', ...xpr.slice(typeRefRootPath.length)];
240
+ if(ctx?.annoExpr?.['='])
241
+ ctx.annoExpr['='] = true;
241
242
  }
242
243
  else {
243
244
  error('odata-anno-xpr-ref', csnPath,
244
- { anno: xprAName, elemref: xpr.join('.'), name: usingPositionStr, code: typeRefStr });
245
+ { anno, elemref: xpr.join('.'), name: usingPositionStr, code: typeRefStr });
245
246
  }
246
247
  }
247
248
  },
@@ -54,6 +54,7 @@ module.exports = (csn, options) => {
54
54
  const ruleToFunction = {
55
55
  __proto__: null,
56
56
  never: skip,
57
+ onlyViaParent: skip, // TODO: not correct
57
58
  onlyViaArtifact,
58
59
  notWithPersistenceTable,
59
60
  };
@@ -579,7 +579,7 @@ function checkFileCase( dep, realpath, nativeRealpath, { warning } ) {
579
579
  }
580
580
  for (const using of dep.usingFroms) {
581
581
  warning('file-unexpected-case-mismatch', [ using.location, using ], {},
582
- // eslint-disable-next-line max-len
582
+ // eslint-disable-next-line @stylistic/js/max-len
583
583
  'The imported filename differs on the filesystem; ensure that capitalization matches the actual file\'s name');
584
584
  }
585
585
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "5.1.2",
3
+ "version": "5.3.0",
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)",
@@ -15,7 +15,7 @@
15
15
  "types": "lib/main.d.ts",
16
16
  "scripts": {
17
17
  "download": "node scripts/downloadANTLR.js",
18
- "gen": "node ./scripts/build.js && node scripts/genGrammarChecksum.js",
18
+ "gen": "node ./scripts/build.js && node scripts/genGrammarChecksum.js && node ./redepage/bin/redepage --pretty --compile lib/gen/CdlParser.js --copy-base-parser lib/parsers/CdlGrammar.g4",
19
19
  "xmakeAfterInstall": "npm run gen",
20
20
  "xmakePrepareRelease": "echo \"$(node scripts/stripReadme.js README.md)\" > README.md && node scripts/assertSnapshotVersioning.js && node scripts/assertChangelog.js && node scripts/cleanup.js --remove-dev",
21
21
  "test": "npm run test:piper",
@@ -31,14 +31,15 @@ view View as select from
31
31
  };
32
32
  ```
33
33
 
34
- Entity `Target` exists more than once in `View`. In the previous example, this
35
- happens through the *direct* sources in the select clause.
34
+ Entity `Target` exists more than once in `View` under different table aliases.
35
+ In the previous example, this happens through the *direct* sources in the
36
+ select clause.
36
37
  Because the original target exists twice in the redirected target, the compiler
37
38
  isn’t able to correctly resolve the redirection due to ambiguities.
38
39
 
39
40
  This can also happen through *indirect* sources. For example if entity `Main`
40
- were to include `Target` then selecting from `Target` just once would be enough
41
- to trigger this error.
41
+ were to include `Target`, then selecting from `Target` just once would be
42
+ enough to trigger this error.
42
43
 
43
44
  ## How to Fix
44
45
 
@@ -53,9 +53,12 @@ CrossJoin
53
53
 
54
54
  ## How to Fix
55
55
 
56
- Ensure that the redirected association points to an entity that is a reasonable
57
- redirection target. That means, the redirection target shouldn't accidentally
58
- make it a to-many association.
56
+ First, ensure that the redirected association points to an entity that is
57
+ a reasonable redirection target. That means, the redirection target shouldn't
58
+ accidentally make it a to-many association.
59
+
60
+ Then add an explicit ON-condition or explicit foreign keys to the redirected
61
+ association. That will silence the compiler message.
59
62
 
60
63
  ## Related Messages
61
64