@sap/cds-compiler 5.6.0 → 5.7.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 (55) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/bin/cdsse.js +1 -0
  3. package/bin/cdsv2m.js +2 -1
  4. package/doc/Versioning.md +4 -4
  5. package/lib/api/options.js +1 -0
  6. package/lib/base/builtins.js +2 -2
  7. package/lib/base/dictionaries.js +1 -2
  8. package/lib/base/keywords.js +3 -1
  9. package/lib/base/lazyload.js +1 -1
  10. package/lib/base/message-registry.js +169 -144
  11. package/lib/base/messages.js +69 -59
  12. package/lib/base/model.js +3 -3
  13. package/lib/base/node-helpers.js +17 -16
  14. package/lib/base/optionProcessorHelper.js +13 -14
  15. package/lib/base/shuffle.js +4 -1
  16. package/lib/checks/structuredAnnoExpressions.js +1 -1
  17. package/lib/compiler/assert-consistency.js +1 -1
  18. package/lib/compiler/builtins.js +2 -1
  19. package/lib/compiler/extend.js +20 -5
  20. package/lib/compiler/resolve.js +45 -9
  21. package/lib/compiler/shared.js +1 -0
  22. package/lib/edm/annotations/edmJson.js +3 -3
  23. package/lib/edm/annotations/genericTranslation.js +5 -1
  24. package/lib/edm/annotations/vocabularyDefinitions.js +2 -2
  25. package/lib/edm/edmUtils.js +2 -1
  26. package/lib/gen/BaseParser.js +32 -32
  27. package/lib/gen/CdlParser.js +1526 -1488
  28. package/lib/json/from-csn.js +2 -0
  29. package/lib/json/to-csn.js +13 -4
  30. package/lib/language/docCommentParser.js +11 -5
  31. package/lib/language/errorStrategy.js +3 -3
  32. package/lib/language/genericAntlrParser.js +2 -0
  33. package/lib/model/csnUtils.js +6 -1
  34. package/lib/optionProcessor.js +5 -1
  35. package/lib/parsers/AstBuildingParser.js +161 -73
  36. package/lib/parsers/CdlGrammar.g4 +129 -85
  37. package/lib/parsers/Lexer.js +5 -3
  38. package/lib/parsers/index.js +1 -1
  39. package/lib/render/toCdl.js +6 -5
  40. package/lib/render/toHdbcds.js +1 -1
  41. package/lib/render/toSql.js +5 -3
  42. package/lib/render/utils/common.js +19 -6
  43. package/lib/render/utils/delta.js +1 -3
  44. package/lib/render/utils/standardDatabaseFunctions.js +576 -0
  45. package/lib/transform/addTenantFields.js +2 -1
  46. package/lib/transform/db/flattening.js +18 -77
  47. package/lib/transform/db/groupByOrderBy.js +2 -2
  48. package/lib/transform/db/rewriteCalculatedElements.js +14 -19
  49. package/lib/transform/db/temporal.js +2 -1
  50. package/lib/transform/odata/adaptAnnotationRefs.js +79 -0
  51. package/lib/transform/odata/createForeignKeys.js +4 -71
  52. package/lib/transform/odata/flattening.js +11 -1
  53. package/lib/transform/transformUtils.js +20 -85
  54. package/package.json +2 -1
  55. package/bin/cds_update_annotations.js +0 -180
@@ -604,12 +604,12 @@ function csnToCdl( csn, options, msg ) {
604
604
  result += element.key ? 'key ' : '';
605
605
  result += element.masked ? 'masked ' : '';
606
606
  result += quoteNonIdentifierOrKeyword(elementName, env);
607
- if (element.val !== undefined) { // enum value
608
- result += ` = ${exprRenderer.renderExpr(element, env)}`;
609
- }
610
- else if (element['#'] !== undefined) { // enum symbol reference
607
+ if (element['#'] !== undefined) { // enum symbol reference
611
608
  result += ` = #${element['#']}`;
612
609
  }
610
+ else if (element.val !== undefined) { // enum value
611
+ result += ` = ${exprRenderer.renderExpr(element, env)}`;
612
+ }
613
613
  else if (!isCalcElement || !isDirectAssocOrComp(element.type) && !element.$filtered && !element.$enclosed) {
614
614
  // If the element is a calculated element _and_ a direct association or
615
615
  // composition, we'd render `Association to F on (cond) = calcValue;` which
@@ -1366,7 +1366,8 @@ function csnToCdl( csn, options, msg ) {
1366
1366
  // If a name exists (either in target or targetAspect), prefer it over rendering elements.
1367
1367
  const elements = artifact.target?.elements || artifact.targetAspect?.elements;
1368
1368
  if (typeof artifact.target === 'string' || typeof artifact.targetAspect === 'string') {
1369
- result += renderAbsolutePath({ ref: [ artifact.target || artifact.targetAspect ] }, env);
1369
+ result += renderAbsolutePath({ ref: [ artifact.target || artifact.targetAspect ] },
1370
+ { ...env, additionalKeywords: [ 'MANY', 'ONE' ] });
1370
1371
  }
1371
1372
  else if (elements) {
1372
1373
  // anonymous aspect, either parseCdl or client CSN.
@@ -1266,7 +1266,7 @@ function toHdbcdsSource( csn, options, messageFunctions ) {
1266
1266
  // we can't quote functions with parens, issue warning if it is a reserved keyword
1267
1267
  if (!funcWithoutParen(x, 'hana') && keywords.hdbcds.includes(uppercaseAndUnderscore(funcName)))
1268
1268
  warning(null, this.env.path, { id: uppercaseAndUnderscore(funcName) }, 'The identifier $(ID) is a SAP HANA keyword');
1269
- return renderFunc(funcName, x, 'hana', a => renderArgs(a, '=>', this.env));
1269
+ return renderFunc(funcName, x, a => renderArgs(a, '=>', this.env), { options });
1270
1270
  }
1271
1271
 
1272
1272
 
@@ -122,8 +122,10 @@ function toSqlDdl( csn, options, messageFunctions ) {
122
122
  return `CAST(${this.renderExpr(withoutCast(x))} AS ${typeRef})`;
123
123
  },
124
124
  val: renderExpressionLiteral,
125
- enum() {
126
- // FIXME: We can't do enums yet because they are not resolved (and we don't bother finding their value by hand)
125
+ enum(x) {
126
+ // visitExpr first checks for `#`, then `val`:
127
+ if (x.val !== undefined)
128
+ return renderExpressionLiteral(x);
127
129
  error('expr-unexpected-enum', this.env.path, 'Enum values are not yet supported for conversion to SQL');
128
130
  return '';
129
131
  },
@@ -135,7 +137,7 @@ function toSqlDdl( csn, options, messageFunctions ) {
135
137
  return renderWindowFunction(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, this.env);
136
138
  },
137
139
  func(x) {
138
- return renderFunc(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, options.sqlDialect, a => renderArgs(a, '=>', this.env, null));
140
+ return renderFunc(smartFuncId(prepareIdentifier(x.func), options.sqlDialect), x, a => renderArgs(a, '=>', this.env, null), { messageFunctions, options, path: this.env.path });
139
141
  },
140
142
  xpr(x) {
141
143
  const env = this.env.withSubPath([ 'xpr' ]);
@@ -3,6 +3,7 @@
3
3
  'use strict';
4
4
 
5
5
  const { ModelError } = require('../../base/error');
6
+ const { standardDatabaseFunctions } = require('./standardDatabaseFunctions');
6
7
 
7
8
  const functionsWithoutParams = {
8
9
  hana: {
@@ -27,13 +28,25 @@ const { implicitAs } = require('../../model/csnRefs');
27
28
  *
28
29
  * @param {string} funcName Name of the function
29
30
  * @param {object} node Content of the function
30
- * @param {string} dialect One of 'hana', 'cap' or 'sqlite' - only 'hana' is relevant atm
31
31
  * @param {(a: string) => string} renderArgs Function to render function arguments
32
+ * @param {object} utils Utility object containing options, path, and message functions
32
33
  * @returns {string} Function string
33
34
  */
34
- function renderFunc( funcName, node, dialect, renderArgs ) {
35
- if (funcWithoutParen( node, dialect ))
35
+ function renderFunc( funcName, node, renderArgs, utils ) {
36
+ const { options, path, messageFunctions } = utils || {};
37
+ const { sqlDialect } = options;
38
+ if (funcWithoutParen( node, sqlDialect ))
36
39
  return funcName;
40
+ const rewriteStandardFunctions = options.transformation !== 'hdbcds' && sqlDialect !== 'plain' && options.standardDatabaseFunctions;
41
+ if (rewriteStandardFunctions) {
42
+ // we check function arguments for correctness
43
+ const { error } = messageFunctions;
44
+ const that = { renderArgs, error, path };
45
+ if (standardDatabaseFunctions[sqlDialect]?.[funcName])
46
+ return standardDatabaseFunctions[sqlDialect][funcName].call(that, node);
47
+ else if (standardDatabaseFunctions.common[funcName])
48
+ return standardDatabaseFunctions.common[funcName].call(that, node);
49
+ }
37
50
  return `${funcName}(${renderArgs( node )})`;
38
51
  }
39
52
 
@@ -682,13 +695,13 @@ function visitExpr( x ) {
682
695
  return result;
683
696
  }).join(', ')})`;
684
697
  }
685
- else if (x.val !== undefined) {
686
- return this.val(x);
687
- }
688
698
  else if (x['#']) {
689
699
  // Enum symbol
690
700
  return this.enum(x);
691
701
  }
702
+ else if (x.val !== undefined) {
703
+ return this.val(x);
704
+ }
692
705
  else if (x.ref) {
693
706
  // Reference: Array of path steps, possibly preceded by ':'
694
707
  return this.ref(x);
@@ -74,9 +74,7 @@ class DeltaRenderer {
74
74
  alterColumns(artifactName, columnName, delta, definitionsStr, _eltName, _env) {
75
75
  if (Array.isArray(definitionsStr)) {
76
76
  const prefix = `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ALTER (`;
77
- let padding = '';
78
- for (let i = 0; i < prefix.length; i++)
79
- padding += ' ';
77
+ const padding = ' '.repeat(prefix.length);
80
78
  const body = definitionsStr.map(s => padding + s).join(',\n').slice(padding.length); // no padding for first part
81
79
  const postfix = ');';
82
80
  return [ prefix + body + postfix ];