@sap/cds-compiler 2.5.2 → 2.11.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 (102) hide show
  1. package/CHANGELOG.md +235 -9
  2. package/bin/cdsc.js +44 -27
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +37 -3
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +37 -123
  7. package/lib/api/options.js +27 -15
  8. package/lib/api/validate.js +34 -9
  9. package/lib/backends.js +9 -89
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +73 -11
  13. package/lib/base/messages.js +86 -30
  14. package/lib/base/model.js +6 -6
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/defaultValues.js +27 -2
  17. package/lib/checks/elements.js +1 -6
  18. package/lib/checks/foreignKeys.js +0 -6
  19. package/lib/checks/managedWithoutKeys.js +17 -0
  20. package/lib/checks/nonexpandableStructured.js +38 -0
  21. package/lib/checks/onConditions.js +9 -45
  22. package/lib/checks/queryNoDbArtifacts.js +25 -7
  23. package/lib/checks/selectItems.js +29 -2
  24. package/lib/checks/types.js +26 -2
  25. package/lib/checks/unknownMagic.js +41 -0
  26. package/lib/checks/utils.js +61 -0
  27. package/lib/checks/validator.js +60 -7
  28. package/lib/compiler/assert-consistency.js +23 -7
  29. package/lib/compiler/base.js +65 -0
  30. package/lib/compiler/builtins.js +30 -1
  31. package/lib/compiler/checks.js +8 -5
  32. package/lib/compiler/definer.js +157 -133
  33. package/lib/compiler/index.js +89 -31
  34. package/lib/compiler/propagator.js +5 -2
  35. package/lib/compiler/resolver.js +375 -185
  36. package/lib/compiler/shared.js +49 -202
  37. package/lib/compiler/utils.js +173 -0
  38. package/lib/edm/annotations/genericTranslation.js +183 -187
  39. package/lib/edm/csn2edm.js +104 -108
  40. package/lib/edm/edm.js +18 -21
  41. package/lib/edm/edmPreprocessor.js +388 -146
  42. package/lib/edm/edmUtils.js +104 -34
  43. package/lib/gen/Dictionary.json +22 -0
  44. package/lib/gen/language.checksum +1 -1
  45. package/lib/gen/language.interp +28 -1
  46. package/lib/gen/language.tokens +79 -69
  47. package/lib/gen/languageLexer.interp +28 -1
  48. package/lib/gen/languageLexer.js +879 -805
  49. package/lib/gen/languageLexer.tokens +71 -62
  50. package/lib/gen/languageParser.js +5330 -4300
  51. package/lib/json/from-csn.js +110 -52
  52. package/lib/json/to-csn.js +434 -120
  53. package/lib/language/antlrParser.js +15 -3
  54. package/lib/language/errorStrategy.js +1 -0
  55. package/lib/language/genericAntlrParser.js +93 -26
  56. package/lib/language/language.g4 +172 -31
  57. package/lib/main.d.ts +216 -19
  58. package/lib/main.js +32 -7
  59. package/lib/model/api.js +78 -0
  60. package/lib/model/csnRefs.js +413 -149
  61. package/lib/model/csnUtils.js +286 -75
  62. package/lib/model/enrichCsn.js +50 -6
  63. package/lib/model/revealInternalProperties.js +22 -5
  64. package/lib/modelCompare/compare.js +39 -21
  65. package/lib/optionProcessor.js +35 -18
  66. package/lib/render/.eslintrc.json +4 -1
  67. package/lib/render/DuplicateChecker.js +9 -6
  68. package/lib/render/toCdl.js +121 -36
  69. package/lib/render/toHdbcds.js +148 -98
  70. package/lib/render/toSql.js +114 -43
  71. package/lib/render/utils/common.js +8 -13
  72. package/lib/render/utils/sql.js +3 -3
  73. package/lib/sql-identifier.js +6 -1
  74. package/lib/transform/db/assertUnique.js +5 -6
  75. package/lib/transform/db/constraints.js +281 -106
  76. package/lib/transform/db/draft.js +11 -8
  77. package/lib/transform/db/expansion.js +584 -0
  78. package/lib/transform/db/flattening.js +341 -0
  79. package/lib/transform/db/groupByOrderBy.js +2 -2
  80. package/lib/transform/db/transformExists.js +345 -65
  81. package/lib/transform/db/views.js +438 -0
  82. package/lib/transform/forHanaNew.js +131 -793
  83. package/lib/transform/forOdataNew.js +30 -24
  84. package/lib/transform/localized.js +39 -10
  85. package/lib/transform/odata/attachPath.js +19 -4
  86. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  87. package/lib/transform/odata/referenceFlattener.js +60 -39
  88. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  89. package/lib/transform/odata/structuralPath.js +72 -0
  90. package/lib/transform/odata/structureFlattener.js +19 -18
  91. package/lib/transform/odata/typesExposure.js +22 -12
  92. package/lib/transform/transformUtilsNew.js +144 -78
  93. package/lib/transform/translateAssocsToJoins.js +22 -27
  94. package/lib/transform/universalCsnEnricher.js +67 -0
  95. package/lib/utils/file.js +5 -14
  96. package/lib/utils/moduleResolve.js +6 -8
  97. package/lib/utils/term.js +65 -42
  98. package/lib/utils/timetrace.js +48 -26
  99. package/package.json +1 -1
  100. package/lib/json/walker.js +0 -26
  101. package/lib/transform/sqlite +0 -0
  102. package/lib/utils/string.js +0 -17
@@ -8,11 +8,13 @@ const {
8
8
  isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
9
9
  } = require('../model/csnUtils');
10
10
  const keywords = require('../base/keywords');
11
- const { renderFunc, processExprArray, findElement } = require('./utils/common');
11
+ const { renderFunc, beautifyExprArray, findElement } = require('./utils/common');
12
12
  const { checkCSNVersion } = require('../json/csnVersion');
13
- const timetrace = require('../utils/timetrace');
13
+ const { timetrace } = require('../utils/timetrace');
14
14
  const { csnRefs } = require('../model/csnRefs');
15
15
  const { forEachDefinition } = require('../model/csnUtils');
16
+ const enrichUniversalCsn = require('../transform/universalCsnEnricher');
17
+ const { isBetaEnabled } = require('../base/model');
16
18
 
17
19
  /**
18
20
  * Render the CSN model 'model' to CDS source text. One source is created per
@@ -33,6 +35,9 @@ function toCdsSourceCsn(csn, options) {
33
35
  // Skip compactModel if already using CSN
34
36
  // const csn = cloneCsn(model, options);
35
37
 
38
+ if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
39
+ enrichUniversalCsn(csn, options);
40
+
36
41
  checkCSNVersion(csn, options);
37
42
 
38
43
  const result = Object.create(null);
@@ -50,16 +55,20 @@ function toCdsSourceCsn(csn, options) {
50
55
  result[main] += `${sourceStr}\n`;
51
56
  });
52
57
 
53
- // Apply possible subelement annotations with an "annotate X with"
58
+ // Apply possible subelement/action return annotations with an "annotate X with"
59
+ // Some of them appear in csn.extensions, some not...
54
60
  if (subelementAnnotates.length > 0) {
55
- for (const [ artName, element, elementName ] of subelementAnnotates) {
56
- let sourceStr = `annotate ${artName} with {\n`;
57
- sourceStr += ` ${elementName} {\n`;
61
+ for (const [ artName, element, elementName, suffix ] of subelementAnnotates) {
62
+ // Suffix is used with action return annotations
63
+ let sourceStr = `annotate ${artName} with ${suffix ? `${suffix} ` : ''}{\n`;
64
+ if (elementName) // action returns do not have element name - we need less {} there
65
+ sourceStr += ` ${elementName} {\n`;
58
66
  const env = increaseIndent(increaseIndent(createEnv()));
59
67
  const subelements = renderSubelementAnnotates(element, env);
60
68
  if (subelements !== '') {
61
69
  sourceStr += `${subelements}\n`;
62
- sourceStr += ' }\n';
70
+ if (elementName) // action returns do not have element name - we need less {} there
71
+ sourceStr += ' }\n';
63
72
  sourceStr += '}\n';
64
73
  result[main] += `${sourceStr}\n`;
65
74
  }
@@ -145,6 +154,12 @@ function toCdsSourceCsn(csn, options) {
145
154
  if (ext.elements)
146
155
  result += renderElementExtensions(ext.elements, env);
147
156
 
157
+ // Returns annotations
158
+ if (ext.returns) {
159
+ const childEnv = increaseIndent(env);
160
+ result += ` with returns${renderElementExtensions(ext.returns.elements, childEnv)}`;
161
+ }
162
+
148
163
  // Action annotations
149
164
  if (ext.actions) {
150
165
  result += ' actions {\n';
@@ -162,10 +177,19 @@ function toCdsSourceCsn(csn, options) {
162
177
 
163
178
  result += `${paramAnnotations.join(',\n')}\n${childEnv.indent})`;
164
179
  }
180
+ // Annotations on action returns
181
+ if (action.returns && action.returns.elements) {
182
+ const grandChildEnv = increaseIndent(childEnv);
183
+ result += ` returns${renderElementExtensions(action.returns.elements, grandChildEnv)}`;
184
+ }
185
+
186
+
165
187
  result += ';\n';
166
188
  }
167
189
  result += `${env.indent}}`;
168
190
  }
191
+
192
+
169
193
  result += ';';
170
194
  return result;
171
195
  }).join('\n');
@@ -205,6 +229,7 @@ function toCdsSourceCsn(csn, options) {
205
229
  function renderArtifact(artifactName, art, env) {
206
230
  // FIXME: Correctly build the paths during runtime to give better locations
207
231
  env.path = [ 'definitions', artifactName ];
232
+ env.artifactName = artifactName;
208
233
 
209
234
  switch (art.kind) {
210
235
  case 'entity':
@@ -509,6 +534,7 @@ function toCdsSourceCsn(csn, options) {
509
534
  * @param {Boolean} [isSubElement]
510
535
  */
511
536
  function renderElement(elementName, elm, env, isSubElement) {
537
+ env.elementName = elementName;
512
538
  let result = renderDocComment(elm, env) + renderAnnotationAssignments(elm, env);
513
539
  const quotedElementName = quoteOrUppercaseId(elementName);
514
540
  result += `${env.indent + (elm.virtual ? 'virtual ' : '') +
@@ -516,10 +542,11 @@ function toCdsSourceCsn(csn, options) {
516
542
  ((elm.masked && !elm._ignoreMasked) ? 'masked ' : '') +
517
543
  quotedElementName} : ${
518
544
  renderTypeReference(elm, env, undefined)
519
- }${renderNullability(elm)}`;
545
+ }${elm.on ? '' : renderNullability(elm)}`;
520
546
  if (elm.default)
521
547
  result += ` default ${renderExpr(elm.default, env)}`;
522
548
 
549
+ delete env.elementName;
523
550
  return `${result};\n`;
524
551
  }
525
552
 
@@ -552,7 +579,7 @@ function toCdsSourceCsn(csn, options) {
552
579
  * @return {string}
553
580
  */
554
581
  function renderQueryActionsAndFunctions(artifactName, art, env) {
555
- let result = renderDocComment(art, env) + renderActionsAndFunctions(art, env);
582
+ let result = renderActionsAndFunctions(art, env);
556
583
  // Even if we have seen actions/functions, they might all have been ignored
557
584
  if (result !== '')
558
585
  result = `${env.indent}extend entity ${artifactName} with${result};`;
@@ -585,7 +612,7 @@ function toCdsSourceCsn(csn, options) {
585
612
  }
586
613
  // Now iterate elements - render an annotation if it is different from the column's
587
614
  const childEnv = increaseIndent(env);
588
- let result = renderDocComment(art, env);
615
+ let result = '';
589
616
  for (const elemName in art.elements) {
590
617
  let elemAnnotations = '';
591
618
  const elem = art.elements[elemName];
@@ -688,7 +715,7 @@ function toCdsSourceCsn(csn, options) {
688
715
 
689
716
  // Even the first step might have parameters and/or a filter
690
717
  if (path.ref[0].args)
691
- result += `(${renderArgs(path.ref[0].args, ':', env)})`;
718
+ result += `(${renderArgs(path.ref[0], ':', env)})`;
692
719
 
693
720
  if (path.ref[0].where)
694
721
  result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env, true, true)}]`;
@@ -739,17 +766,13 @@ function toCdsSourceCsn(csn, options) {
739
766
  result += 'virtual ';
740
767
 
741
768
  const key = col.key ? 'key ' : '';
742
- result += key + renderExpr(col, env, true);
769
+ // Use special rendering for .expand/.inline - renderExpr cannot easily handle some cases
770
+ result += key + ((col.expand || col.inline) ? renderInlineExpand(col, env) : renderExpr(col, env, true));
743
771
 
744
- if (col.as)
772
+ // Alias is already handled by renderInlineExpand
773
+ if (!col.inline && !col.expand && col.as)
745
774
  result += ` as ${quoteOrUppercaseId(col.as)}`;
746
775
 
747
- if (col.inline)
748
- renderInlineExpand('inline');
749
-
750
- if (col.expand)
751
- renderInlineExpand('expand');
752
-
753
776
  // Explicit type provided for the view element?
754
777
  if (col.cast) {
755
778
  // Special case: Explicit association type is actually a redirect
@@ -764,24 +787,69 @@ function toCdsSourceCsn(csn, options) {
764
787
  }
765
788
  }
766
789
  return result;
790
+ }
767
791
 
768
- /**
769
- * For the current column, render a (nested) inline/expand.
770
- *
771
- * @param {'inline'|'expand'} prop
772
- */
773
- function renderInlineExpand(prop) {
774
- if (prop === 'inline')
792
+ /**
793
+ * For the current column, render a (nested) inline/expand. If the current column
794
+ * does not have an .expand/.inline, '' is returned
795
+ *
796
+ * @param {object} col Thing with .expand or .inline
797
+ * @param {CdlRenderEnvironment} parentEnv
798
+ * @returns {string}
799
+ */
800
+ function renderInlineExpand(col, parentEnv) {
801
+ if (!col.inline && !col.expand)
802
+ return '';
803
+
804
+ return renderIX(col, parentEnv);
805
+
806
+ function renderIX(obj, env) {
807
+ // No expression to render for { * } as alias
808
+ let result = (obj.as && obj.expand && !obj.ref) ? '' : renderExpr(obj, env);
809
+
810
+ // s as alias { * }
811
+ if (obj.as && (obj.ref || obj.xpr || obj.val !== undefined || obj.func !== undefined))
812
+ result += ` as ${obj.as}`;
813
+
814
+ // We found a leaf - no further drilling
815
+ if (!obj.inline && !obj.expand) {
816
+ if (obj.cast && obj.cast.type) {
817
+ result += ` : ${renderTypeReference(obj.cast, createEnv())}`;
818
+ }
819
+ else if (obj.cast && obj.cast.target) { // test tbd
820
+ result += ` : redirected to ${renderAbsoluteNameWithQuotes(obj.cast.target)}`;
821
+ if (obj.cast.on)
822
+ result += ` on ${renderExpr(obj.cast.on, env, true, true)}`;
823
+ else if (obj.cast.keys)
824
+ result += ` { ${Object.keys(obj.cast.keys).map(name => renderForeignKey(obj.cast.keys[name], env)).join(', ')} }`;
825
+ }
826
+ return result;
827
+ }
828
+
829
+ if (obj.inline)
775
830
  result += '.{\n';
776
831
  else
777
- result += ' {\n';
832
+ result += result !== '' ? ' {\n' : '{\n';
778
833
 
779
- col[prop].forEach((elm, i) => {
780
- result += `${renderViewColumn(elm, increaseIndent(env))}`;
781
- if (i < col[prop].length - 1)
834
+ // Drill down and render children of the expand/inline
835
+ const childEnv = increaseIndent(env);
836
+ const expandInline = obj.expand || obj.inline;
837
+ expandInline.forEach((elm, i) => {
838
+ result += `${childEnv.indent}${renderIX(elm, childEnv)}`;
839
+ if (i < expandInline.length - 1)
782
840
  result += ',\n';
783
841
  });
784
842
  result += `\n${env.indent}}`;
843
+
844
+ // Don't forget about the .excluding
845
+ if (obj.excluding)
846
+ result += ` excluding { ${obj.excluding.join(',')} }`;
847
+
848
+ // { * } as expand
849
+ if (!obj.ref && obj.as)
850
+ result += ` as ${obj.as}`;
851
+
852
+ return result;
785
853
  }
786
854
  }
787
855
 
@@ -1015,8 +1083,11 @@ function toCdsSourceCsn(csn, options) {
1015
1083
  const childEnv = increaseIndent(env);
1016
1084
  const parameters = Object.keys(act.params || []).map(name => renderParameter(name, act.params[name], childEnv)).join(',\n');
1017
1085
  result += (parameters === '') ? '()' : `(\n${parameters}\n${env.indent})`;
1018
- if (act.returns)
1086
+ if (act.returns) {
1087
+ if (act.returns.type && act.returns.elements) // action returns annotations
1088
+ subelementAnnotates.push([ actionName, act.returns, '', 'returns' ]);
1019
1089
  result += ` returns ${renderTypeReference(act.returns, env)}${renderNullability(act.returns)}`;
1090
+ }
1020
1091
 
1021
1092
  result += ';\n';
1022
1093
  return result;
@@ -1092,6 +1163,10 @@ function toCdsSourceCsn(csn, options) {
1092
1163
  let rc = `many ${renderTypeReference(elm.items, env)}`;
1093
1164
  if (elm.items.notNull != null)
1094
1165
  rc += elm.items.notNull ? ' not null' : ' null';
1166
+ // many sub element annotates
1167
+ if (elm.items.type && elm.items.elements && env.artifactName)
1168
+ subelementAnnotates.push([ env.artifactName, elm.items, env.elementName ]);
1169
+
1095
1170
  return rc;
1096
1171
  }
1097
1172
 
@@ -1275,7 +1350,7 @@ function toCdsSourceCsn(csn, options) {
1275
1350
  function renderExpr(x, env, inline = true, inExpr = false) {
1276
1351
  // Compound expression
1277
1352
  if (Array.isArray(x))
1278
- return processExprArray(x, renderExpr, env, inline, inExpr);
1353
+ return beautifyExprArray(x.map(item => renderExpr(item, env, inline, inExpr)));
1279
1354
 
1280
1355
  if (typeof x === 'object' && x !== null) {
1281
1356
  if (inExpr && x.cast && x.cast.type)
@@ -1329,6 +1404,12 @@ function toCdsSourceCsn(csn, options) {
1329
1404
  // FIXME: no extra magic with x.param or x.global
1330
1405
  return `${(x.param || x.global) ? ':' : ''}${x.ref.map(renderPathStep).join('.')}`;
1331
1406
  }
1407
+ else if (x.xpr && x.func) {
1408
+ // window function
1409
+ const funcDef = renderFunc( x.func, x, null, a => renderArgs(a, '=>', env) );
1410
+ const windowFunctionOperator = x.xpr.shift(); // OVER ...
1411
+ return `${funcDef} ${windowFunctionOperator} ( ${renderExpr(x.xpr, env, true)} )`;
1412
+ }
1332
1413
  // Function call, possibly with args (use '=>' for named args)
1333
1414
  else if (x.func) {
1334
1415
  // test for non-regular HANA identifier that needs to be quoted
@@ -1399,18 +1480,19 @@ function toCdsSourceCsn(csn, options) {
1399
1480
 
1400
1481
  // Not really a path step but an object-like function call
1401
1482
  if (s.func)
1402
- return `${s.func}(${renderArgs(s.args, '=>', env)})`;
1483
+ return `${s.func}(${renderArgs(s, '=>', env)})`;
1403
1484
 
1404
1485
  // Path step, possibly with view parameters and/or filters
1405
1486
  let result = `${quoteOrUppercaseId(s.id)}`;
1406
1487
  if (s.args) {
1407
1488
  // View parameters
1408
- result += `(${renderArgs(s.args, ':', env)})`;
1489
+ result += `(${renderArgs(s, ':', env)})`;
1409
1490
  }
1410
1491
  if (s.where) {
1411
1492
  // Filter, possibly with cardinality
1412
1493
  result += `[${s.cardinality ? (`${s.cardinality.max}: `) : ''}${renderExpr(s.where, env, inline, true)}]`;
1413
1494
  }
1495
+
1414
1496
  return result;
1415
1497
  }
1416
1498
 
@@ -1422,12 +1504,13 @@ function toCdsSourceCsn(csn, options) {
1422
1504
  * Render function arguments or view parameters (positional if array, named if object/dict),
1423
1505
  * using 'sep' as separator for positional parameters
1424
1506
  *
1425
- * @param {object[]|object} args (Un)Named arguments
1507
+ * @param {object} node with `args` to render
1426
1508
  * @param {string} sep
1427
1509
  * @param {CdlRenderEnvironment} env
1428
1510
  * @returns {string}
1429
1511
  */
1430
- function renderArgs(args, sep, env) {
1512
+ function renderArgs(node, sep, env) {
1513
+ const args = node.args ? node.args : {};
1431
1514
  // Positional arguments
1432
1515
  if (Array.isArray(args))
1433
1516
  return args.map(arg => renderExpr(arg, env)).join(', ');
@@ -1819,6 +1902,8 @@ function toCdsSourceCsn(csn, options) {
1819
1902
  *
1820
1903
  * @property {string} indent Current indentation as a string, e.g. ' ' for two spaces.
1821
1904
  * @property {CSN.Path} [path] CSN path to the current artifact
1905
+ * @property {string} artifactName Name of the artifact - set in renderArtifact
1906
+ * @property {string} elementName Name of the element being rendered - set in renderElement
1822
1907
  * @property {{[name: string]: {
1823
1908
  quotedName: string,
1824
1909
  quotedAlias: string