@sap/cds-compiler 2.5.0 → 2.10.4

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 (92) hide show
  1. package/CHANGELOG.md +191 -9
  2. package/bin/cdsc.js +2 -2
  3. package/doc/CHANGELOG_BETA.md +33 -3
  4. package/lib/api/main.js +29 -101
  5. package/lib/api/options.js +15 -11
  6. package/lib/api/validate.js +12 -8
  7. package/lib/backends.js +0 -81
  8. package/lib/base/keywords.js +32 -2
  9. package/lib/base/message-registry.js +63 -9
  10. package/lib/base/messages.js +63 -21
  11. package/lib/base/model.js +2 -3
  12. package/lib/checks/defaultValues.js +27 -2
  13. package/lib/checks/elements.js +1 -6
  14. package/lib/checks/foreignKeys.js +0 -6
  15. package/lib/checks/managedWithoutKeys.js +17 -0
  16. package/lib/checks/nonexpandableStructured.js +38 -0
  17. package/lib/checks/onConditions.js +9 -45
  18. package/lib/checks/queryNoDbArtifacts.js +25 -7
  19. package/lib/checks/selectItems.js +25 -2
  20. package/lib/checks/types.js +26 -2
  21. package/lib/checks/unknownMagic.js +38 -0
  22. package/lib/checks/utils.js +61 -0
  23. package/lib/checks/validator.js +60 -7
  24. package/lib/compiler/assert-consistency.js +16 -7
  25. package/lib/compiler/builtins.js +2 -0
  26. package/lib/compiler/checks.js +6 -4
  27. package/lib/compiler/definer.js +99 -42
  28. package/lib/compiler/index.js +73 -27
  29. package/lib/compiler/resolver.js +288 -157
  30. package/lib/compiler/shared.js +31 -11
  31. package/lib/edm/annotations/genericTranslation.js +182 -186
  32. package/lib/edm/csn2edm.js +103 -108
  33. package/lib/edm/edm.js +18 -21
  34. package/lib/edm/edmPreprocessor.js +361 -114
  35. package/lib/edm/edmUtils.js +103 -33
  36. package/lib/gen/Dictionary.json +22 -0
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +12 -1
  39. package/lib/gen/language.tokens +57 -53
  40. package/lib/gen/languageLexer.interp +10 -1
  41. package/lib/gen/languageLexer.js +770 -744
  42. package/lib/gen/languageLexer.tokens +49 -46
  43. package/lib/gen/languageParser.js +4713 -4279
  44. package/lib/json/from-csn.js +103 -45
  45. package/lib/json/to-csn.js +296 -117
  46. package/lib/language/antlrParser.js +4 -3
  47. package/lib/language/errorStrategy.js +1 -0
  48. package/lib/language/genericAntlrParser.js +21 -12
  49. package/lib/language/language.g4 +99 -31
  50. package/lib/main.d.ts +81 -3
  51. package/lib/main.js +30 -7
  52. package/lib/model/api.js +78 -0
  53. package/lib/model/csnRefs.js +329 -142
  54. package/lib/model/csnUtils.js +235 -58
  55. package/lib/model/enrichCsn.js +18 -1
  56. package/lib/model/revealInternalProperties.js +2 -1
  57. package/lib/modelCompare/compare.js +37 -20
  58. package/lib/optionProcessor.js +9 -3
  59. package/lib/render/.eslintrc.json +4 -1
  60. package/lib/render/DuplicateChecker.js +8 -5
  61. package/lib/render/toCdl.js +112 -33
  62. package/lib/render/toHdbcds.js +134 -64
  63. package/lib/render/toSql.js +91 -38
  64. package/lib/render/utils/common.js +8 -13
  65. package/lib/render/utils/sql.js +3 -3
  66. package/lib/sql-identifier.js +6 -1
  67. package/lib/transform/db/assertUnique.js +5 -6
  68. package/lib/transform/db/constraints.js +29 -13
  69. package/lib/transform/db/draft.js +8 -6
  70. package/lib/transform/db/expansion.js +582 -0
  71. package/lib/transform/db/flattening.js +325 -0
  72. package/lib/transform/db/groupByOrderBy.js +2 -2
  73. package/lib/transform/db/transformExists.js +284 -63
  74. package/lib/transform/forHanaNew.js +98 -381
  75. package/lib/transform/forOdataNew.js +21 -22
  76. package/lib/transform/localized.js +37 -10
  77. package/lib/transform/odata/attachPath.js +19 -4
  78. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  79. package/lib/transform/odata/referenceFlattener.js +60 -39
  80. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  81. package/lib/transform/odata/structuralPath.js +72 -0
  82. package/lib/transform/odata/structureFlattener.js +19 -18
  83. package/lib/transform/odata/typesExposure.js +22 -12
  84. package/lib/transform/transformUtilsNew.js +134 -78
  85. package/lib/transform/translateAssocsToJoins.js +17 -14
  86. package/lib/transform/universalCsnEnricher.js +67 -0
  87. package/lib/utils/file.js +0 -11
  88. package/lib/utils/moduleResolve.js +6 -8
  89. package/package.json +1 -1
  90. package/lib/json/walker.js +0 -26
  91. package/lib/transform/sqlite +0 -0
  92. package/lib/utils/string.js +0 -17
@@ -3,12 +3,12 @@
3
3
  const {
4
4
  getParentNameOf, getLastPartOf, getLastPartOfRef,
5
5
  hasValidSkipOrExists, isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
6
- getRootArtifactName, getResultingName, getNamespace,
6
+ getRootArtifactName, getResultingName, getNamespace, forEachMember,
7
7
  } = require('../model/csnUtils');
8
8
  const keywords = require('../base/keywords');
9
9
  const {
10
- renderFunc, processExprArray, getRealName, addContextMarkers, addIntermediateContexts, cdsToSqlTypes,
11
- hasHanaComment, getHanaComment, findElement,
10
+ renderFunc, beautifyExprArray, getRealName, addContextMarkers, addIntermediateContexts, cdsToSqlTypes,
11
+ hasHanaComment, getHanaComment, findElement, funcWithoutParen,
12
12
  } = require('./utils/common');
13
13
  const {
14
14
  renderReferentialConstraint,
@@ -19,6 +19,8 @@ const { checkCSNVersion } = require('../json/csnVersion');
19
19
  const { makeMessageFunction } = require('../base/messages');
20
20
  const timetrace = require('../utils/timetrace');
21
21
 
22
+ const { smartId, delimitedId } = require('../sql-identifier');
23
+
22
24
  const $PROJECTION = '$projection';
23
25
  const $SELF = '$self';
24
26
 
@@ -39,8 +41,7 @@ function getEscapedHanaComment(obj) {
39
41
  * { "foo" : "using XY; context foo {...};",
40
42
  * "bar::wiz" : "namespace bar::; entity wiz {...};"
41
43
  * }
42
- * If 'options.toHana' is set, render HANA-ish source dialect (currently
43
- * only affects translation of '$self.foo' in paths and ::-ish namespace declarations)
44
+ *
44
45
  * FIXME: This comment no longer tells the whole truth
45
46
  *
46
47
  * @param {CSN.Model} csn HANA transformed CSN
@@ -49,9 +50,9 @@ function getEscapedHanaComment(obj) {
49
50
  */
50
51
  function toHdbcdsSource(csn, options) {
51
52
  timetrace.start('HDBCDS rendering');
52
- const plainNames = options.toHana && options.toHana.names === 'plain';
53
- const quotedNames = options.toHana && options.toHana.names === 'quoted';
54
- const hdbcdsNames = options.toHana && options.toHana.names === 'hdbcds';
53
+ const plainNames = options.sqlMapping === 'plain';
54
+ const quotedNames = options.sqlMapping === 'quoted';
55
+ const hdbcdsNames = options.sqlMapping === 'hdbcds';
55
56
 
56
57
  const {
57
58
  info, warning, error, throwWithError,
@@ -62,7 +63,7 @@ function toHdbcdsSource(csn, options) {
62
63
  const result = Object.create(null);
63
64
 
64
65
 
65
- const globalDuplicateChecker = new DuplicateChecker(options.toHana.names); // registry for all artifact names and element names
66
+ const globalDuplicateChecker = new DuplicateChecker(options.sqlMapping); // registry for all artifact names and element names
66
67
 
67
68
  const killList = [];
68
69
  if (quotedNames)
@@ -389,7 +390,7 @@ function toHdbcdsSource(csn, options) {
389
390
  const childEnv = increaseIndent(env);
390
391
  const normalizedArtifactName = renderArtifactName(artifactName, env);
391
392
 
392
- globalDuplicateChecker.addArtifact(art['@cds.persistence.name'], art && art.$location, artifactName);
393
+ globalDuplicateChecker.addArtifact(art['@cds.persistence.name'], env.path, artifactName);
393
394
 
394
395
  if (hasHanaComment(art, options))
395
396
  result += `${env.indent}@Comment: '${getEscapedHanaComment(art)}'\n`;
@@ -401,7 +402,11 @@ function toHdbcdsSource(csn, options) {
401
402
  }
402
403
  result += ' {\n';
403
404
  const duplicateChecker = new DuplicateChecker(); // registry for all artifact names and element names
404
- duplicateChecker.addArtifact(artifactName, art && art.$location, artifactName);
405
+ duplicateChecker.addArtifact(artifactName, env.path, artifactName);
406
+ childEnv.path = env.path.concat('elements');
407
+ // calculate __aliases which must be used in case an association
408
+ // has the same identifier as it's target
409
+ createTopLevelAliasesForArtifact(artifactName, art, env);
405
410
  for (const name in art.elements)
406
411
  result += renderElement(name, art.elements[name], childEnv, duplicateChecker);
407
412
 
@@ -411,6 +416,33 @@ function toHdbcdsSource(csn, options) {
411
416
  return result;
412
417
  }
413
418
 
419
+ /**
420
+ * If an association/composition has the same identifier as it's target
421
+ * we must render an "using target as __target" and use the alias to refer to the target
422
+ *
423
+ * @param {string} artName
424
+ * @param {CSN.Artifact} art
425
+ * @param {CdlRenderEnvironment} env
426
+ */
427
+ function createTopLevelAliasesForArtifact(artName, art, env) {
428
+ forEachMember(art, (element) => {
429
+ if (!element.target)
430
+ return;
431
+
432
+ let alias = element['@cds.persistence.name'];
433
+ if (uppercaseAndUnderscore(element.target) === element['@cds.persistence.name']) {
434
+ alias = createTopLevelAliasName(element['@cds.persistence.name']);
435
+ // calculate new alias if it would conflict with other csn.Artifact
436
+ while (csn.definitions[alias])
437
+ alias = createTopLevelAliasName(alias);
438
+ env.topLevelAliases[element['@cds.persistence.name']] = {
439
+ quotedName: formatIdentifier(element['@cds.persistence.name']),
440
+ quotedAlias: formatIdentifier(alias),
441
+ };
442
+ }
443
+ });
444
+ }
445
+
414
446
  /**
415
447
  * Render the 'technical configuration { ... }' section 'tc' of an entity.
416
448
  *
@@ -516,9 +548,9 @@ function toHdbcdsSource(csn, options) {
516
548
  // Special handling for HANA CDS: Must omit the ':' before anonymous structured types (for historical reasons)
517
549
  const omitColon = (!elm.type && elm.elements);
518
550
  let result = '';
519
- const quotedElementName = quoteOrUppercaseId(elementName, elm.$location);
551
+ const quotedElementName = formatIdentifier(elementName);
520
552
  if (duplicateChecker)
521
- duplicateChecker.addElement(quotedElementName, elm && elm.$location, elementName);
553
+ duplicateChecker.addElement(quotedElementName, env.path, elementName);
522
554
 
523
555
  if (hasHanaComment(elm, options))
524
556
  result += `${env.indent}@Comment: '${getEscapedHanaComment(elm)}'\n`;
@@ -548,7 +580,7 @@ function toHdbcdsSource(csn, options) {
548
580
  if (source.SELECT || source.SET) {
549
581
  let result = `(${renderQuery(source, false, increaseIndent(env))})`;
550
582
  if (source.as)
551
- result += ` as ${quoteOrUppercaseId(source.as)}`;
583
+ result += ` as ${formatIdentifier(source.as)}`;
552
584
 
553
585
  return result;
554
586
  }
@@ -598,7 +630,7 @@ function toHdbcdsSource(csn, options) {
598
630
 
599
631
  // Even the first step might have parameters and/or a filter
600
632
  if (path.ref[0].args)
601
- result += `(${renderArgs(path.ref[0].args, ':', env)})`;
633
+ result += `(${renderArgs(path.ref[0], ':', env)})`;
602
634
 
603
635
  if (path.ref[0].where)
604
636
  result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env, true, true)}]`;
@@ -624,14 +656,14 @@ function toHdbcdsSource(csn, options) {
624
656
  function renderAbsolutePathWithAlias(path, env) {
625
657
  let result = renderAbsolutePath(path, env);
626
658
  // Take care of aliases - for artifact references, use the resulting name (multi-dot joined with _)
627
- const implicitAlias = path.ref.length === 0 ? getLastPartOf(getResultingName(csn, options.toHana.names, path.ref[0])) : getLastPartOfRef(path.ref);
659
+ const implicitAlias = path.ref.length === 0 ? getLastPartOf(getResultingName(csn, options.sqlMapping, path.ref[0])) : getLastPartOfRef(path.ref);
628
660
  if (path.as) {
629
661
  // Source had an alias - render it
630
- result += ` as ${quoteOrUppercaseId(path.as)}`;
662
+ result += ` as ${formatIdentifier(path.as)}`;
631
663
  }
632
- else if (getLastPartOf(result) !== quoteOrUppercaseId(implicitAlias)) {
664
+ else if (getLastPartOf(result) !== formatIdentifier(implicitAlias)) {
633
665
  // Render an artificial alias if the result would produce a different one
634
- result += ` as ${quoteOrUppercaseId(implicitAlias)}`;
666
+ result += ` as ${formatIdentifier(implicitAlias)}`;
635
667
  }
636
668
  return result;
637
669
  }
@@ -653,9 +685,9 @@ function toHdbcdsSource(csn, options) {
653
685
  const leaf = col.as || col.ref && col.ref[col.ref.length - 1];
654
686
  // Render 'null as <alias>' only for database and if element is virtual
655
687
 
656
- if (element && element.virtual) {
688
+ if (element && element.virtual || env._artifact.elements[leaf] && env._artifact.elements[leaf].virtual) {
657
689
  if (isDeprecatedEnabled(options, 'renderVirtualElements'))
658
- return `${result}${env.indent}null as ${quoteOrUppercaseId(leaf)}`;
690
+ return `${result}${env.indent}null as ${formatIdentifier(leaf)}`;
659
691
  }
660
692
  else {
661
693
  return renderNonVirtualColumn();
@@ -683,7 +715,7 @@ function toHdbcdsSource(csn, options) {
683
715
  alias = leaf;
684
716
 
685
717
  if (alias)
686
- result += ` as ${quoteOrUppercaseId(alias)}`;
718
+ result += ` as ${formatIdentifier(alias)}`;
687
719
 
688
720
  // Explicit type provided for the view element?
689
721
  if (col.cast && col.cast.target) {
@@ -710,7 +742,8 @@ function toHdbcdsSource(csn, options) {
710
742
  */
711
743
  function renderView(artifactName, art, env) {
712
744
  let result = '';
713
- globalDuplicateChecker.addArtifact(art['@cds.persistence.name'], art.$location, artifactName);
745
+ const artifactPath = [ 'definitions', artifactName ];
746
+ globalDuplicateChecker.addArtifact(art['@cds.persistence.name'], artifactPath, artifactName);
714
747
 
715
748
  if (hasHanaComment(art, options))
716
749
  result += `${env.indent}@Comment: '${getEscapedHanaComment(art)}'\n`;
@@ -726,7 +759,7 @@ function toHdbcdsSource(csn, options) {
726
759
  result += ' as ';
727
760
  }
728
761
  env._artifact = art;
729
- result += renderQuery(getNormalizedQuery(art).query, true, env, [ 'definitions', artifactName, 'query' ], art.elements);
762
+ result += renderQuery(getNormalizedQuery(art).query, true, env, artifactPath.concat(art.projection ? 'projection' : 'query'), art.elements);
730
763
  result += ';\n';
731
764
  return result;
732
765
  }
@@ -776,7 +809,6 @@ function toHdbcdsSource(csn, options) {
776
809
  const childEnv = increaseIndent(env);
777
810
  childEnv.currentArtifactName = $PROJECTION; // $self to be replaced by $projection
778
811
  result += `select from ${renderViewSource(select.from, env)}`;
779
-
780
812
  if (select.mixin) {
781
813
  let elems = '';
782
814
  for (const name in select.mixin)
@@ -797,7 +829,7 @@ function toHdbcdsSource(csn, options) {
797
829
  result += `${env.indent}}`;
798
830
  }
799
831
  if (select.excluding) {
800
- result += ` excluding {\n${select.excluding.map(id => `${childEnv.indent}${quoteOrUppercaseId(id)}`).join(',\n')}\n`;
832
+ result += ` excluding {\n${select.excluding.map(id => `${childEnv.indent}${formatIdentifier(id)}`).join(',\n')}\n`;
801
833
  result += `${env.indent}}`;
802
834
  }
803
835
 
@@ -895,7 +927,7 @@ function toHdbcdsSource(csn, options) {
895
927
  function renderParameter(parName, par, env) {
896
928
  if (par.notNull === true || par.notNull === false)
897
929
  info(null, env.path.concat([ 'params', parName ]), 'Not Null constraints on HDBCDS view parameters are not allowed and are ignored');
898
- return `${env.indent + quoteOrUppercaseId(parName)} : ${renderTypeReference(par, env)}`;
930
+ return `${env.indent + formatParamIdentifier(parName, env.path.concat([ 'params', parName ]))} : ${renderTypeReference(par, env)}`;
899
931
  }
900
932
 
901
933
  /**
@@ -1012,10 +1044,24 @@ function toHdbcdsSource(csn, options) {
1012
1044
 
1013
1045
  result += `${renderCardinality(elm.cardinality)} to `;
1014
1046
 
1047
+
1015
1048
  // normal target or named aspect
1016
1049
  if (elm.target || elm.targetAspect && typeof elm.targetAspect === 'string') {
1017
- result += plainNames ? renderAbsoluteNamePlain(elm.target || elm.targetAspect, env)
1018
- : renderAbsoluteNameWithQuotes(elm.target || elm.targetAspect, env);
1050
+ // we might have a "using target as __target"
1051
+ const targetArtifact = csn.definitions[elm.target];
1052
+ const targetAlias = env.topLevelAliases[targetArtifact['@cds.persistence.name']];
1053
+ if (targetAlias) {
1054
+ result += targetAlias.quotedAlias;
1055
+ }
1056
+ else {
1057
+ result += plainNames ? renderAbsoluteNamePlain(elm.target || elm.targetAspect, env)
1058
+ : renderAbsoluteNameWithQuotes(elm.target || elm.targetAspect, env);
1059
+ }
1060
+ }
1061
+
1062
+ // ON-condition (if any)
1063
+ if (elm.on) {
1064
+ result += ` on ${renderExpr(elm.on, env, true, true)}`;
1019
1065
  }
1020
1066
  else if (elm.targetAspect && elm.targetAspect.elements) { // anonymous aspect
1021
1067
  const childEnv = increaseIndent(env);
@@ -1027,11 +1073,6 @@ function toHdbcdsSource(csn, options) {
1027
1073
  }
1028
1074
 
1029
1075
 
1030
- // ON-condition (if any)
1031
- if (elm.on)
1032
- result += ` on ${renderExpr(elm.on, env, true, true)}`;
1033
-
1034
-
1035
1076
  // Foreign keys (if any, unless we also have an ON_condition (which means we have been transformed from managed to unmanaged)
1036
1077
  if (elm.keys && !elm.on)
1037
1078
  result += ` { ${Object.keys(elm.keys).map(name => renderForeignKey(elm.keys[name], env)).join(', ')} }`;
@@ -1087,7 +1128,7 @@ function toHdbcdsSource(csn, options) {
1087
1128
  function renderExpr(x, env, inline = true, inExpr = false) {
1088
1129
  // Compound expression
1089
1130
  if (Array.isArray(x))
1090
- return processExprArray(x, renderExpr, env, inline, inExpr);
1131
+ return beautifyExprArray(x.map(item => renderExpr(item, env, inline, inExpr)));
1091
1132
 
1092
1133
  if (typeof x === 'object' && x !== null) {
1093
1134
  if (inExpr && x.cast && x.cast.type)
@@ -1129,6 +1170,9 @@ function toHdbcdsSource(csn, options) {
1129
1170
 
1130
1171
  const regex = RegExp(/^[a-zA-Z][\w#$]*$/, 'g');
1131
1172
  const funcName = regex.test(x.func) ? x.func : quoteId(x.func);
1173
+ // we can't quote functions with parens, issue warning if it is a reserved keyword
1174
+ if (!funcWithoutParen(x, 'hana') && keywords.hdbcds.includes(uppercaseAndUnderscore(funcName)))
1175
+ warning(null, x.$location, `The identifier “${uppercaseAndUnderscore(funcName)}“ is a SAP HANA keyword`);
1132
1176
  return renderFunc( funcName, x, 'hana', a => renderArgs(a, '=>', env) );
1133
1177
  }
1134
1178
  // Nested expression
@@ -1188,11 +1232,11 @@ function toHdbcdsSource(csn, options) {
1188
1232
  if (x.ref[0] === '$user') {
1189
1233
  // FIXME: this is all not enough: we might need an explicit select item alias
1190
1234
  if (x.ref[1] === 'id') {
1191
- if (options.toHana.user && typeof options.toHana.user === 'string' || options.toHana.user instanceof String)
1192
- return `'${options.toHana.user}'`;
1235
+ if (options.magicVars && options.magicVars.user && (typeof options.magicVars.user === 'string' || options.magicVars.user instanceof String))
1236
+ return `'${options.magicVars.user}'`;
1193
1237
 
1194
- else if ((options.toHana.user && options.toHana.user.id) && (typeof options.toHana.user.id === 'string' || options.toHana.user.id instanceof String))
1195
- return `'${options.toHana.user.id}'`;
1238
+ else if ((options.magicVars && options.magicVars.user && options.magicVars.user.id) && (typeof options.magicVars.user.id === 'string' || options.magicVars.user.id instanceof String))
1239
+ return `'${options.magicVars.user.id}'`;
1196
1240
 
1197
1241
  return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
1198
1242
  }
@@ -1267,7 +1311,7 @@ function toHdbcdsSource(csn, options) {
1267
1311
  [ $SELF, $PROJECTION, '$session' ].includes(s))
1268
1312
  return s;
1269
1313
 
1270
- return quoteOrUppercaseId(s);
1314
+ return formatIdentifier(s);
1271
1315
  }
1272
1316
  // ID with filters or parameters
1273
1317
  else if (typeof s === 'object') {
@@ -1277,13 +1321,13 @@ function toHdbcdsSource(csn, options) {
1277
1321
 
1278
1322
  // Not really a path step but an object-like function call
1279
1323
  if (s.func)
1280
- return `${s.func}(${renderArgs(s.args, '=>', env)})`;
1324
+ return `${s.func}(${renderArgs(s, '=>', env)})`;
1281
1325
 
1282
1326
  // Path step, possibly with view parameters and/or filters
1283
- let result = `${quoteOrUppercaseId(s.id)}`;
1327
+ let result = `${formatIdentifier(s.id)}`;
1284
1328
  if (s.args) {
1285
1329
  // View parameters
1286
- result += `(${renderArgs(s.args, ':', env)})`;
1330
+ result += `(${renderArgs(s, ':', env)})`;
1287
1331
  }
1288
1332
  if (s.where) {
1289
1333
  // Filter, possibly with cardinality
@@ -1300,19 +1344,21 @@ function toHdbcdsSource(csn, options) {
1300
1344
  * Render function arguments or view parameters (positional if array, named if object/dict),
1301
1345
  * using 'sep' as separator for positional parameters
1302
1346
  *
1303
- * @param {object[]|object} args (Un)Named arguments
1347
+ * @param {object} node with `args` to render
1304
1348
  * @param {string} sep Seperator between arguments
1305
1349
  * @param {CdlRenderEnvironment} env Environment
1306
1350
  * @returns {string} Rendered arguments
1307
1351
  */
1308
- function renderArgs(args, sep, env) {
1352
+ function renderArgs(node, sep, env) {
1353
+ const args = node.args ? node.args : {};
1309
1354
  // Positional arguments
1310
1355
  if (Array.isArray(args))
1311
1356
  return args.map(arg => renderExpr(arg, env)).join(', ');
1312
1357
 
1313
1358
  // Named arguments (object/dict)
1314
1359
  else if (typeof args === 'object')
1315
- return Object.keys(args).map(key => `${quoteOrUppercaseId(key)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
1360
+ // if this is a function param which is not a reference to the model, we must not quote it
1361
+ return Object.keys(args).map(key => `${node.func ? key : formatIdentifier(key)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
1316
1362
 
1317
1363
 
1318
1364
  throw new Error(`Unknown args: ${JSON.stringify(args)}`);
@@ -1405,10 +1451,10 @@ function toHdbcdsSource(csn, options) {
1405
1451
  function renderAbsoluteNamePlain(absName, env) {
1406
1452
  // Add using declaration
1407
1453
  env.topLevelAliases[absName] = {
1408
- quotedName: uppercaseAndUnderscore(absName),
1409
- quotedAlias: uppercaseAndUnderscore(absName),
1454
+ quotedName: formatIdentifier(uppercaseAndUnderscore(absName)),
1455
+ quotedAlias: formatIdentifier(uppercaseAndUnderscore(absName)),
1410
1456
  };
1411
- return uppercaseAndUnderscore(absName);
1457
+ return formatIdentifier(uppercaseAndUnderscore(absName));
1412
1458
  }
1413
1459
 
1414
1460
  /**
@@ -1493,7 +1539,7 @@ function toHdbcdsSource(csn, options) {
1493
1539
  function renderUsings(artifactName, env) {
1494
1540
  const distinct = {};
1495
1541
  Object.keys(env.topLevelAliases)
1496
- .filter(name => !(plainNames && env.topLevelAliases[name].quotedName === uppercaseAndUnderscore(artifactName))) // avoid "using FOO as FOO" in FOO.cds
1542
+ .filter(name => env.topLevelAliases[name].quotedAlias !== formatIdentifier(uppercaseAndUnderscore(artifactName))) // avoid "using FOO as FOO" in FOO.cds
1497
1543
  .forEach((name) => {
1498
1544
  distinct[`using ${env.topLevelAliases[name].quotedName} as ${env.topLevelAliases[name].quotedAlias};\n`] = '';
1499
1545
  });
@@ -1564,6 +1610,8 @@ function toHdbcdsSource(csn, options) {
1564
1610
  topLevelAliases: Object.create(null),
1565
1611
  // Current name prefix (including trailing dot if not empty)
1566
1612
  namePrefix: '',
1613
+ // CSN path - should at least point to the correct artifact
1614
+ path: [],
1567
1615
  };
1568
1616
  }
1569
1617
 
@@ -1608,7 +1656,7 @@ function toHdbcdsSource(csn, options) {
1608
1656
  */
1609
1657
  function quoteAbsolutePathString(abspath) {
1610
1658
  const namespace = getNamespace(csn, abspath);
1611
- const resultingName = getResultingName(csn, options.toHana.names, abspath);
1659
+ const resultingName = getResultingName(csn, options.sqlMapping, abspath);
1612
1660
 
1613
1661
  if (hdbcdsNames && namespace)
1614
1662
  return `${quotePathString(namespace)}::${quotePathString(resultingName.slice(namespace.length + 2))}`;
@@ -1633,10 +1681,19 @@ function toHdbcdsSource(csn, options) {
1633
1681
  return id;
1634
1682
 
1635
1683
 
1636
- return `"${id.replace(/"/g, '""')}"`;
1684
+ switch (options.forHana.names) {
1685
+ case 'plain':
1686
+ return smartId(id, 'hdbcds');
1687
+ case 'quoted':
1688
+ return delimitedId(id, 'hdbcds');
1689
+ case 'hdbcds':
1690
+ return delimitedId(id, 'hdbcds');
1691
+ default:
1692
+ return null;
1693
+ }
1637
1694
  }
1638
1695
 
1639
- /**
1696
+ /*
1640
1697
  * Return an absolute name 'absname', with '::' inserted if required by naming strategy 'hdbcds', quoted
1641
1698
  * as if it was a single identifier (required only for native USINGs)
1642
1699
  *
@@ -1654,21 +1711,34 @@ function toHdbcdsSource(csn, options) {
1654
1711
  }
1655
1712
 
1656
1713
  /**
1657
- * Quote or uppercase an identifier 'id', depending on naming strategy
1714
+ * Quote and/or uppercase an identifier 'id', depending on naming strategy
1658
1715
  *
1659
1716
  * @param {string} id Identifier
1660
- * @param {CSN.Location} [location] Optional location for the warning.
1661
1717
  * @returns {string} Quoted/uppercased id
1662
1718
  */
1663
- function quoteOrUppercaseId(id, location = null) {
1664
- if (plainNames) {
1665
- const result = uppercaseAndUnderscore(id);
1666
- // Warn if colliding with HANA keyword
1667
- if (keywords.hana.includes(result))
1668
- warning(null, location, `The identifier "${id}" is a SAP HANA keyword`);
1719
+ function formatIdentifier(id) {
1720
+ id = options.forHana.names === 'plain' ? id.toUpperCase() : id;
1721
+ return quoteId(id);
1722
+ }
1723
+
1724
+ /**
1725
+ * Quote or uppercase a parameter identifier 'id', depending on naming strategy
1726
+ * Smart quoting cannot be applied to the parameter identifiers, issue warning instead.
1727
+ *
1728
+ *
1729
+ * @param {string} id Identifier
1730
+ * @param {CSN.Path} [location] Optional location for the warning.
1731
+ * @returns {string} Quoted/uppercased id
1732
+ */
1733
+ function formatParamIdentifier(id, location) {
1734
+ // Warn if colliding with HANA keyword, but do not quote for plain
1735
+ // --> quoted reserved words as param lead to a weird deployment error
1736
+ if (keywords.hdbcds.includes(uppercaseAndUnderscore(id)))
1737
+ warning(null, location, { id }, 'The identifier $(ID) is a SAP HANA keyword');
1738
+
1739
+ if (plainNames)
1740
+ return uppercaseAndUnderscore(id);
1669
1741
 
1670
- return result;
1671
- }
1672
1742
  return quoteId(id);
1673
1743
  }
1674
1744
 
@@ -1691,7 +1761,7 @@ function toHdbcdsSource(csn, options) {
1691
1761
  */
1692
1762
  function renderArtifactName(artifactName, env, fallthrough = false) {
1693
1763
  if (plainNames && !fallthrough)
1694
- return uppercaseAndUnderscore(artifactName);
1764
+ return formatIdentifier(uppercaseAndUnderscore(artifactName));
1695
1765
  // hdbcds with quoted or hdbcds naming
1696
1766
  return env.namePrefix + quoteId(getRealName(csn, artifactName).replace(/\./g, '_'));
1697
1767
  }