@sap/cds-compiler 2.4.4 → 2.10.2

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 (106) hide show
  1. package/CHANGELOG.md +241 -1
  2. package/bin/.eslintrc.json +17 -0
  3. package/bin/cds_update_identifiers.js +8 -7
  4. package/bin/cdsc.js +180 -132
  5. package/bin/cdshi.js +18 -11
  6. package/bin/cdsse.js +38 -32
  7. package/bin/cdsv2m.js +8 -7
  8. package/doc/CHANGELOG_BETA.md +36 -1
  9. package/lib/api/main.js +81 -100
  10. package/lib/api/options.js +17 -11
  11. package/lib/api/validate.js +12 -8
  12. package/lib/backends.js +0 -81
  13. package/lib/base/keywords.js +32 -2
  14. package/lib/base/location.js +2 -2
  15. package/lib/base/message-registry.js +66 -4
  16. package/lib/base/messages.js +84 -27
  17. package/lib/base/model.js +2 -61
  18. package/lib/checks/arrayOfs.js +0 -1
  19. package/lib/checks/defaultValues.js +27 -2
  20. package/lib/checks/elements.js +1 -6
  21. package/lib/checks/enricher.js +8 -2
  22. package/lib/checks/foreignKeys.js +0 -6
  23. package/lib/checks/managedWithoutKeys.js +17 -0
  24. package/lib/checks/nonexpandableStructured.js +38 -0
  25. package/lib/checks/onConditions.js +9 -45
  26. package/lib/checks/queryNoDbArtifacts.js +27 -9
  27. package/lib/checks/selectItems.js +25 -2
  28. package/lib/checks/types.js +26 -2
  29. package/lib/checks/unknownMagic.js +38 -0
  30. package/lib/checks/utils.js +61 -0
  31. package/lib/checks/validator.js +66 -13
  32. package/lib/compiler/assert-consistency.js +24 -12
  33. package/lib/compiler/builtins.js +2 -0
  34. package/lib/compiler/checks.js +6 -4
  35. package/lib/compiler/definer.js +101 -39
  36. package/lib/compiler/index.js +88 -59
  37. package/lib/compiler/resolver.js +455 -209
  38. package/lib/compiler/shared.js +57 -33
  39. package/lib/edm/annotations/genericTranslation.js +183 -187
  40. package/lib/edm/csn2edm.js +128 -99
  41. package/lib/edm/edm.js +18 -21
  42. package/lib/edm/edmPreprocessor.js +361 -127
  43. package/lib/edm/edmUtils.js +103 -33
  44. package/lib/gen/Dictionary.json +74 -28
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +18 -4
  47. package/lib/gen/language.tokens +124 -118
  48. package/lib/gen/languageLexer.interp +13 -1
  49. package/lib/gen/languageLexer.js +870 -839
  50. package/lib/gen/languageLexer.tokens +116 -111
  51. package/lib/gen/languageParser.js +5894 -5614
  52. package/lib/json/from-csn.js +152 -67
  53. package/lib/json/to-csn.js +334 -135
  54. package/lib/language/antlrParser.js +4 -3
  55. package/lib/language/errorStrategy.js +1 -0
  56. package/lib/language/genericAntlrParser.js +24 -14
  57. package/lib/language/language.g4 +188 -128
  58. package/lib/main.d.ts +435 -0
  59. package/lib/main.js +31 -7
  60. package/lib/model/api.js +78 -0
  61. package/lib/model/csnRefs.js +463 -187
  62. package/lib/model/csnUtils.js +280 -136
  63. package/lib/model/enrichCsn.js +75 -4
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/modelCompare/compare.js +70 -25
  66. package/lib/optionProcessor.js +13 -10
  67. package/lib/render/.eslintrc.json +4 -1
  68. package/lib/render/DuplicateChecker.js +8 -5
  69. package/lib/render/toCdl.js +123 -40
  70. package/lib/render/toHdbcds.js +156 -65
  71. package/lib/render/toSql.js +87 -11
  72. package/lib/render/utils/common.js +55 -9
  73. package/lib/render/utils/sql.js +3 -3
  74. package/lib/sql-identifier.js +6 -1
  75. package/lib/transform/{sql → db}/.eslintrc.json +0 -0
  76. package/lib/transform/{sql → db}/assertUnique.js +7 -8
  77. package/lib/transform/{sql → db}/constraints.js +35 -20
  78. package/lib/transform/db/draft.js +353 -0
  79. package/lib/transform/db/expansion.js +582 -0
  80. package/lib/transform/db/flattening.js +325 -0
  81. package/lib/transform/{sql → db}/groupByOrderBy.js +8 -16
  82. package/lib/transform/{sql → db}/helpers.js +0 -0
  83. package/lib/transform/{sql → db}/transformExists.js +256 -60
  84. package/lib/transform/forHanaNew.js +216 -765
  85. package/lib/transform/forOdataNew.js +60 -56
  86. package/lib/transform/localized.js +48 -26
  87. package/lib/transform/odata/attachPath.js +19 -4
  88. package/lib/transform/odata/expandStructKeysInAssociations.js +2 -2
  89. package/lib/transform/odata/generateForeignKeyElements.js +13 -12
  90. package/lib/transform/odata/referenceFlattener.js +60 -36
  91. package/lib/transform/odata/sortByAssociationDependency.js +4 -4
  92. package/lib/transform/odata/structuralPath.js +76 -0
  93. package/lib/transform/odata/structureFlattener.js +21 -22
  94. package/lib/transform/odata/toFinalBaseType.js +5 -5
  95. package/lib/transform/odata/typesExposure.js +27 -17
  96. package/lib/transform/odata/utils.js +2 -2
  97. package/lib/transform/transformUtilsNew.js +141 -77
  98. package/lib/transform/translateAssocsToJoins.js +17 -14
  99. package/lib/transform/universalCsnEnricher.js +67 -0
  100. package/lib/utils/file.js +0 -11
  101. package/lib/utils/moduleResolve.js +6 -8
  102. package/lib/utils/timetrace.js +6 -1
  103. package/package.json +2 -1
  104. package/lib/base/deepCopy.js +0 -66
  105. package/lib/json/walker.js +0 -26
  106. 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 } = require('./utils/common');
11
+ const { renderFunc, beautifyExprArray, findElement } = require('./utils/common');
12
12
  const { checkCSNVersion } = require('../json/csnVersion');
13
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
 
@@ -727,27 +754,25 @@ function toCdsSourceCsn(csn, options) {
727
754
  *
728
755
  * @param {object} col
729
756
  * @param {CdlRenderEnvironment} env
757
+ * @param {CSN.Element} element Element corresponding to the column. Generated by the compiler.
730
758
  */
731
- function renderViewColumn(col, env) {
759
+ function renderViewColumn(col, env, element) {
732
760
  // Annotations and column
733
- let result = renderDocComment(col, env) + renderAnnotationAssignments(col, env);
761
+ let result = renderDocComment(col.doc ? col : element, env) + renderAnnotationAssignments(col, env);
734
762
  result += env.indent;
763
+
735
764
  // only if column is virtual, keyword virtual was present in the source text
736
765
  if (col.virtual)
737
766
  result += 'virtual ';
738
767
 
739
- const key = (!env.skipKeys && col.key ? 'key ' : '');
740
- result += key + renderExpr(col, env, true);
768
+ const key = col.key ? 'key ' : '';
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));
741
771
 
742
- if (col.as)
772
+ // Alias is already handled by renderInlineExpand
773
+ if (!col.inline && !col.expand && col.as)
743
774
  result += ` as ${quoteOrUppercaseId(col.as)}`;
744
775
 
745
- if (col.inline)
746
- renderInlineExpand('inline');
747
-
748
- if (col.expand)
749
- renderInlineExpand('expand');
750
-
751
776
  // Explicit type provided for the view element?
752
777
  if (col.cast) {
753
778
  // Special case: Explicit association type is actually a redirect
@@ -762,24 +787,69 @@ function toCdsSourceCsn(csn, options) {
762
787
  }
763
788
  }
764
789
  return result;
790
+ }
765
791
 
766
- /**
767
- * For the current column, render a (nested) inline/expand.
768
- *
769
- * @param {'inline'|'expand'} prop
770
- */
771
- function renderInlineExpand(prop) {
772
- 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)
773
830
  result += '.{\n';
774
831
  else
775
- result += ' {\n';
832
+ result += result !== '' ? ' {\n' : '{\n';
776
833
 
777
- col[prop].forEach((elm, i) => {
778
- result += `${renderViewColumn(elm, increaseIndent(env))}`;
779
- 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)
780
840
  result += ',\n';
781
841
  });
782
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;
783
853
  }
784
854
  }
785
855
 
@@ -791,8 +861,11 @@ function toCdsSourceCsn(csn, options) {
791
861
  * @returns {String}
792
862
  */
793
863
  function renderDocComment(obj, env) {
794
- if (!(obj && obj.doc))
864
+ if (!obj || obj && obj.doc === undefined)
795
865
  return '';
866
+ else if (obj && obj.doc === null) // empty doc comment needs to be rendered
867
+ return `\n${env.indent}/** */\n`;
868
+
796
869
  return `\n${env.indent}/**\n${obj.doc.split('\n').map(line => `${env.indent} * ${line}`).join('\n')}\n${env.indent} */\n`;
797
870
  }
798
871
 
@@ -818,7 +891,7 @@ function toCdsSourceCsn(csn, options) {
818
891
  result += ' as ';
819
892
  }
820
893
  env._artifact = art;
821
- result += renderQuery(getNormalizedQuery(art).query, true, syntax, env, [ 'definitions', artifactName, 'query' ]);
894
+ result += renderQuery(getNormalizedQuery(art).query, true, syntax, env, [ 'definitions', artifactName, 'query' ], art.elements);
822
895
  result += ';\n';
823
896
  result += renderQueryElementAnnotations(artifactName, art, env);
824
897
  result += renderQueryActionsAndFunctions(artifactName, art, env);
@@ -837,18 +910,17 @@ function toCdsSourceCsn(csn, options) {
837
910
  * @param {CdlRenderEnvironment} env
838
911
  * @param {CSN.Path} [path=[]]
839
912
  */
840
- function renderQuery(query, isLeadingQuery, syntax, env, path = []) {
913
+ function renderQuery(query, isLeadingQuery, syntax, env, path = [], elements = query.elements || Object.create(null)) {
841
914
  let result = renderDocComment(query, env);
842
- env.skipKeys = !isLeadingQuery;
843
915
  // Set operator, like UNION, INTERSECT, ...
844
916
  if (query.SET) {
845
917
  // First arg may be leading query
846
- result += `(${renderQuery(query.SET.args[0], isLeadingQuery, 'view', env, path.concat([ 'SET', 'args', 0 ]))}`;
918
+ result += `(${renderQuery(query.SET.args[0], isLeadingQuery, 'view', env, path.concat([ 'SET', 'args', 0 ]), elements)}`;
847
919
  // FIXME: Clarify if set operators can be n-ary (assuming binary here)
848
920
  if (query.SET.op) {
849
921
  // Loop over all other arguments, i.e. for A UNION B UNION C UNION D ...
850
922
  for (let i = 1; i < query.SET.args.length; i++)
851
- result += `\n${env.indent}${query.SET.op}${query.SET.all ? ' all' : ''} ${renderQuery(query.SET.args[i], false, 'view', env, path.concat([ 'SET', 'args', i ]))}`;
923
+ result += `\n${env.indent}${query.SET.op}${query.SET.all ? ' all' : ''} ${renderQuery(query.SET.args[i], false, 'view', env, path.concat([ 'SET', 'args', i ]), elements)}`;
852
924
  }
853
925
  result += ')';
854
926
  // Set operation may also have an ORDER BY and LIMIT/OFFSET (in contrast to the ones belonging to
@@ -890,7 +962,7 @@ function toCdsSourceCsn(csn, options) {
890
962
  result += select.distinct ? ' distinct' : '';
891
963
  if (select.columns) {
892
964
  result += ' {\n';
893
- result += `${select.columns.map(col => renderViewColumn(col, childEnv))
965
+ result += `${select.columns.map(col => renderViewColumn(col, childEnv, findElement(elements, col)))
894
966
  .filter(s => s !== '')
895
967
  .join(',\n')}\n`;
896
968
  result += `${env.indent}}`;
@@ -1011,8 +1083,11 @@ function toCdsSourceCsn(csn, options) {
1011
1083
  const childEnv = increaseIndent(env);
1012
1084
  const parameters = Object.keys(act.params || []).map(name => renderParameter(name, act.params[name], childEnv)).join(',\n');
1013
1085
  result += (parameters === '') ? '()' : `(\n${parameters}\n${env.indent})`;
1014
- 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' ]);
1015
1089
  result += ` returns ${renderTypeReference(act.returns, env)}${renderNullability(act.returns)}`;
1090
+ }
1016
1091
 
1017
1092
  result += ';\n';
1018
1093
  return result;
@@ -1080,7 +1155,7 @@ function toCdsSourceCsn(csn, options) {
1080
1155
  * @return {string}
1081
1156
  */
1082
1157
  function renderTypeReference(elm, env, noEnum = false) {
1083
- let result = renderDocComment(elm, env);
1158
+ let result = '';
1084
1159
 
1085
1160
  // Array type: Render items instead
1086
1161
  if (elm.items && !elm.type) {
@@ -1088,6 +1163,10 @@ function toCdsSourceCsn(csn, options) {
1088
1163
  let rc = `many ${renderTypeReference(elm.items, env)}`;
1089
1164
  if (elm.items.notNull != null)
1090
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
+
1091
1170
  return rc;
1092
1171
  }
1093
1172
 
@@ -1205,10 +1284,11 @@ function toCdsSourceCsn(csn, options) {
1205
1284
  * @return {string}
1206
1285
  */
1207
1286
  function renderEnum(enumPart, env) {
1208
- let result = `${renderDocComment(enumPart, env)} enum {\n`;
1287
+ let result = ' enum {\n';
1209
1288
  const childEnv = increaseIndent(env);
1210
1289
  for (const name in enumPart) {
1211
1290
  const enumConst = enumPart[name];
1291
+ result += renderDocComment(enumConst, childEnv);
1212
1292
  result += renderAnnotationAssignments(enumConst, childEnv);
1213
1293
  result += childEnv.indent + quoteIdIfRequired(name);
1214
1294
  if (enumConst.val !== undefined)
@@ -1270,7 +1350,7 @@ function toCdsSourceCsn(csn, options) {
1270
1350
  function renderExpr(x, env, inline = true, inExpr = false) {
1271
1351
  // Compound expression
1272
1352
  if (Array.isArray(x))
1273
- return processExprArray(x, renderExpr, env, inline, inExpr);
1353
+ return beautifyExprArray(x.map(item => renderExpr(item, env, inline, inExpr)));
1274
1354
 
1275
1355
  if (typeof x === 'object' && x !== null) {
1276
1356
  if (inExpr && x.cast && x.cast.type)
@@ -1406,6 +1486,7 @@ function toCdsSourceCsn(csn, options) {
1406
1486
  // Filter, possibly with cardinality
1407
1487
  result += `[${s.cardinality ? (`${s.cardinality.max}: `) : ''}${renderExpr(s.where, env, inline, true)}]`;
1408
1488
  }
1489
+
1409
1490
  return result;
1410
1491
  }
1411
1492
 
@@ -1814,6 +1895,8 @@ function toCdsSourceCsn(csn, options) {
1814
1895
  *
1815
1896
  * @property {string} indent Current indentation as a string, e.g. ' ' for two spaces.
1816
1897
  * @property {CSN.Path} [path] CSN path to the current artifact
1898
+ * @property {string} artifactName Name of the artifact - set in renderArtifact
1899
+ * @property {string} elementName Name of the element being rendered - set in renderElement
1817
1900
  * @property {{[name: string]: {
1818
1901
  quotedName: string,
1819
1902
  quotedAlias: string