@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
@@ -4,10 +4,10 @@
4
4
  const {
5
5
  getLastPartOf, getLastPartOfRef,
6
6
  hasValidSkipOrExists, isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
7
- forEachDefinition, getResultingName,
7
+ forEachDefinition, getResultingName, getVariableReplacement,
8
8
  } = require('../model/csnUtils');
9
9
  const {
10
- renderFunc, processExprArray, cdsToSqlTypes, getHanaComment, hasHanaComment,
10
+ renderFunc, beautifyExprArray, cdsToSqlTypes, getHanaComment, hasHanaComment,
11
11
  } = require('./utils/common');
12
12
  const {
13
13
  renderReferentialConstraint, getIdentifierUtils,
@@ -15,7 +15,7 @@ const {
15
15
  const DuplicateChecker = require('./DuplicateChecker');
16
16
  const { checkCSNVersion } = require('../json/csnVersion');
17
17
  const { makeMessageFunction } = require('../base/messages');
18
- const timetrace = require('../utils/timetrace');
18
+ const { timetrace } = require('../utils/timetrace');
19
19
  const { isBetaEnabled, isDeprecatedEnabled } = require('../base/model');
20
20
  const { smartFuncId } = require('../sql-identifier');
21
21
  const { sortCsn } = require('../json/to-csn');
@@ -152,6 +152,24 @@ function toSqlDdl(csn, options) {
152
152
  .join(', ');
153
153
  return primaryKeys && `PRIMARY KEY(${primaryKeys})`;
154
154
  },
155
+ /*
156
+ Render entity-comment modifications as HANA SQL.
157
+ */
158
+ alterEntityComment(tableName, comment) {
159
+ return [ `COMMENT ON TABLE ${tableName} IS ${render.comment(comment)};` ];
160
+ },
161
+ /*
162
+ Render column-comment modifications as HANA SQL.
163
+ */
164
+ alterColumnComment(tableName, columnName, comment) {
165
+ return [ `COMMENT ON COLUMN ${tableName}.${columnName} IS ${render.comment(comment)};` ];
166
+ },
167
+ /*
168
+ Render comment string.
169
+ */
170
+ comment(comment) {
171
+ return comment && `'${comment.replace(/'/g, '\'\'')}'` || 'NULL';
172
+ },
155
173
  /*
156
174
  Concatenate multiple statements which are to be treated as one by the API caller.
157
175
  */
@@ -211,8 +229,9 @@ function toSqlDdl(csn, options) {
211
229
  for (const extension of options && options.testMode ? sortCsn(csn.extensions) : csn.extensions) {
212
230
  if (extension.extend) {
213
231
  const artifactName = extension.extend;
214
- const env = { indent: '' };
215
- renderArtifactExtensionInto(artifactName, csn.definitions[artifactName], extension, resultObj, env);
232
+ const _artifact = csn.definitions[artifactName];
233
+ const env = { indent: '', _artifact };
234
+ renderArtifactExtensionInto(artifactName, _artifact, extension, resultObj, env);
216
235
  }
217
236
  }
218
237
  }
@@ -223,7 +242,8 @@ function toSqlDdl(csn, options) {
223
242
  for (const migration of options && options.testMode ? sortCsn(csn.migrations) : csn.migrations) {
224
243
  if (migration.migrate) {
225
244
  const artifactName = migration.migrate;
226
- const env = { indent: '' };
245
+ const _artifact = csn.definitions[artifactName];
246
+ const env = { indent: '', _artifact };
227
247
  renderArtifactMigrationInto(artifactName, migration, resultObj, env);
228
248
  }
229
249
  }
@@ -365,12 +385,32 @@ function toSqlDdl(csn, options) {
365
385
  function reducesTypeSize(def) {
366
386
  // HANA does not allow decreasing the value of any of those type parameters.
367
387
  return def.old.type === def.new.type &&
368
- [ 'length', 'precision', 'scale' ].some(param => def.new[param] < def.old[param]);
388
+ [ 'length', 'precision', 'scale' ].some(param => def.new[param] < def.old[param]);
389
+ }
390
+ function getEltStr(defVariant, eltName) {
391
+ return defVariant.target
392
+ ? renderAssociationElement(eltName, defVariant, env)
393
+ : renderElement(artifactName, eltName, defVariant, null, null, env);
394
+ }
395
+ function getEltStrNoProp(defVariant, prop, eltName) {
396
+ const defNoProp = Object.assign({}, defVariant);
397
+ delete defNoProp[prop];
398
+ return getEltStr(defNoProp, eltName);
369
399
  }
370
400
 
371
401
  const tableName = renderArtifactName(artifactName);
372
402
 
373
- // Drop column (unsupported in sqlite)
403
+ // Change entity properties
404
+ if (migration.properties) {
405
+ for (const [ prop, def ] of Object.entries(migration.properties)) {
406
+ if (prop === 'doc') {
407
+ const alterComment = render.alterEntityComment(tableName, def.new);
408
+ addMigration(resultObj, artifactName, false, alterComment);
409
+ }
410
+ }
411
+ }
412
+
413
+ // Drop columns (unsupported in sqlite)
374
414
  if (migration.remove) {
375
415
  const entries = Object.entries(migration.remove);
376
416
  if (entries.length) {
@@ -389,22 +429,28 @@ function toSqlDdl(csn, options) {
389
429
  }
390
430
  }
391
431
 
392
- // Change column type (unsupported in sqlite)
432
+ // Change column types (unsupported in sqlite)
393
433
  if (migration.change) {
394
434
  changeElementsDuplicateChecker.addArtifact(tableName, undefined, artifactName);
395
435
  for (const [ eltName, def ] of Object.entries(migration.change)) {
396
436
  const sqlId = quoteSqlId(eltName);
397
437
  changeElementsDuplicateChecker.addElement(sqlId, undefined, eltName);
398
438
 
399
- const eltStrOld = def.old.target
400
- ? renderAssociationElement(eltName, def.old, env)
401
- : renderElement(artifactName, eltName, def.old, null, null, env);
402
- const eltStrNew = def.new.target
403
- ? renderAssociationElement(eltName, def.new, env)
404
- : renderElement(artifactName, eltName, def.new, null, null, env);
439
+ const eltStrOld = getEltStr(def.old, eltName);
440
+ const eltStrNew = getEltStr(def.new, eltName);
405
441
  if (eltStrNew === eltStrOld)
406
442
  return; // Prevent spurious migrations, where the column DDL does not change.
407
443
 
444
+ if (def.old.doc !== def.new.doc) {
445
+ const eltStrOldNoDoc = getEltStrNoProp(def.old, 'doc', eltName);
446
+ const eltStrNewNoDoc = getEltStrNoProp(def.new, 'doc', eltName);
447
+ if (eltStrOldNoDoc === eltStrNewNoDoc) { // only `doc` changed
448
+ const alterComment = render.alterColumnComment(tableName, sqlId, def.new.doc);
449
+ addMigration(resultObj, artifactName, false, alterComment);
450
+ continue;
451
+ }
452
+ }
453
+
408
454
  if (options.sqlChangeMode === 'drop' || def.old.target || def.new.target || reducesTypeSize(def)) {
409
455
  // Lossy change because either an association is removed and/or added, or the type size is reduced.
410
456
  // Drop old element and re-add it in its new shape.
@@ -479,12 +525,12 @@ function toSqlDdl(csn, options) {
479
525
  referentialConstraints[fileName] = renderReferentialConstraint(referentialConstraint, childEnv.indent, false, csn, options);
480
526
  });
481
527
  if (renderReferentialConstraintsAsHdbconstraint) {
482
- Object.entries(referentialConstraints).forEach( ([ fileName, constraint ]) => {
528
+ Object.entries(referentialConstraints).forEach(([ fileName, constraint ]) => {
483
529
  resultObj.hdbconstraint[fileName] = constraint;
484
530
  });
485
531
  }
486
532
  else {
487
- Object.values(referentialConstraints).forEach( (constraint) => {
533
+ Object.values(referentialConstraints).forEach((constraint) => {
488
534
  result += `,\n${constraint}`;
489
535
  });
490
536
  }
@@ -500,8 +546,7 @@ function toSqlDdl(csn, options) {
500
546
  = `UNIQUE INVERTED INDEX ${renderArtifactName(`${artifactName}_${cn}`)} ON ${tableName} (${c.map(cpath => quoteSqlId(cpath.ref[0])).join(', ')})`;
501
547
  }
502
548
  else {
503
- result += `,\n${childEnv.indent}CONSTRAINT ${renderArtifactName(`${artifactName}_${cn}`)} UNIQUE (${
504
- c.map(cpath => quoteSqlId(cpath.ref[0])).join(', ')})`;
549
+ result += `,\n${childEnv.indent}CONSTRAINT ${renderArtifactName(`${artifactName}_${cn}`)} UNIQUE (${c.map(cpath => quoteSqlId(cpath.ref[0])).join(', ')})`;
505
550
  }
506
551
  }
507
552
  result += `${env.indent}\n)`;
@@ -522,7 +567,7 @@ function toSqlDdl(csn, options) {
522
567
  if (options.toSql.dialect === 'hana')
523
568
  renderIndexesInto(art.technicalConfig && art.technicalConfig.hana.indexes, artifactName, resultObj, env);
524
569
 
525
- if (options.toSql.dialect === 'hana' && hasHanaComment(art, options, art))
570
+ if (options.toSql.dialect === 'hana' && hasHanaComment(art, options))
526
571
  result += ` COMMENT '${getHanaComment(art)}'`;
527
572
 
528
573
  resultObj.hdbtable[artifactName] = result;
@@ -613,8 +658,7 @@ function toSqlDdl(csn, options) {
613
658
  if (duplicateChecker)
614
659
  duplicateChecker.addElement(quotedElementName, elm.$location, elementName);
615
660
 
616
- let result = `${env.indent + quotedElementName} ${
617
- renderTypeReference(artifactName, elementName, elm)
661
+ let result = `${env.indent + quotedElementName} ${renderTypeReference(artifactName, elementName, elm)
618
662
  }${renderNullability(elm, true)}`;
619
663
  if (elm.default)
620
664
  result += ` DEFAULT ${renderExpr(elm.default, env)}`;
@@ -910,7 +954,7 @@ function toSqlDdl(csn, options) {
910
954
  // An empty actual parameter list is rendered as `()`.
911
955
  const ref = csn.definitions[path.ref[0].id] || csn.definitions[path.ref[0]];
912
956
  if (ref && ref.params) {
913
- result += `(${renderArgs(path.ref[0].args || {}, '=>', env, syntax)})`;
957
+ result += `(${renderArgs(path.ref[0] || {}, '=>', env, syntax)})`;
914
958
  }
915
959
  else if ([ 'udf' ].includes(syntax)) {
916
960
  // if syntax is user defined function, render empty argument list
@@ -932,21 +976,23 @@ function toSqlDdl(csn, options) {
932
976
  * Render function arguments or view parameters (positional if array, named if object/dict),
933
977
  * using 'sep' as separator for positional parameters
934
978
  *
935
- * @param {Array|object} args Arguments to render
979
+ * @param {object} node with `args` to render
936
980
  * @param {string} sep Separator between args
937
981
  * @param {object} env Render environment
938
982
  * @param {string} syntax Some magic A2J paramter - for calcview parameter rendering
939
983
  * @returns {string} Rendered arguments
940
984
  * @throws Throws if args is not an array or object.
941
985
  */
942
- function renderArgs(args, sep, env, syntax) {
986
+ function renderArgs(node, sep, env, syntax) {
987
+ const args = node.args ? node.args : {};
943
988
  // Positional arguments
944
989
  if (Array.isArray(args))
945
990
  return args.map(arg => renderExpr(arg, env)).join(', ');
946
991
 
947
992
  // Named arguments (object/dict)
948
993
  else if (typeof args === 'object')
949
- return Object.keys(args).map(key => `${decorateParameter(key, syntax)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
994
+ // if this is a function param which is not a reference to the model, we must not quote it
995
+ return Object.keys(args).map(key => `${node.func ? key : decorateParameter(key, syntax)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
950
996
 
951
997
 
952
998
  throw new Error(`Unknown args: ${JSON.stringify(args)}`);
@@ -1008,7 +1054,7 @@ function toSqlDdl(csn, options) {
1008
1054
  definitionsDuplicateChecker.addArtifact(art['@cds.persistence.name'], art && art.$location, artifactName);
1009
1055
  let result = `VIEW ${viewName}`;
1010
1056
 
1011
- if (options.toSql.dialect === 'hana' && hasHanaComment(art, options, art))
1057
+ if (options.toSql.dialect === 'hana' && hasHanaComment(art, options))
1012
1058
  result += ` COMMENT '${getHanaComment(art)}'`;
1013
1059
 
1014
1060
  result += renderParameterDefinitions(artifactName, art.params);
@@ -1042,8 +1088,15 @@ function toSqlDdl(csn, options) {
1042
1088
  const p = params[pn];
1043
1089
  if (p.notNull === true || p.notNull === false)
1044
1090
  info(null, [ 'definitions', artifactName, 'params', pn ], 'Not Null constraints on SQL view parameters are not allowed and are ignored');
1045
-
1046
- let pstr = `IN ${prepareIdentifier(pn)} ${renderTypeReference(artifactName, pn, p)}`;
1091
+ // do not quote parameter identifiers for naming mode "quoted" / "hdbcds"
1092
+ // this would be an incompatible change, as non-uppercased, quoted identifiers
1093
+ // are rejected by the HANA compiler.
1094
+ let pIdentifier;
1095
+ if (options.toSql.names === 'quoted' || options.toSql.names === 'hdbcds')
1096
+ pIdentifier = prepareIdentifier(pn);
1097
+ else
1098
+ pIdentifier = quoteSqlId(pn);
1099
+ let pstr = `IN ${pIdentifier} ${renderTypeReference(artifactName, pn, p)}`;
1047
1100
  if (p.default)
1048
1101
  pstr += ` DEFAULT ${renderExpr(p.default)}`;
1049
1102
 
@@ -1098,12 +1151,11 @@ function toSqlDdl(csn, options) {
1098
1151
  const childEnv = increaseIndent(env);
1099
1152
  result += `SELECT${select.distinct ? ' DISTINCT' : ''}`;
1100
1153
  // FIXME: We probably also need to consider `excluding` here ?
1101
- result += `\n${
1102
- (select.columns || [ '*' ])
1103
- .filter(col => !(select.mixin || Object.create(null))[firstPathStepId(col.ref)]) // No mixin columns
1104
- .map(col => renderViewColumn(col, childEnv))
1105
- .filter(s => s !== '')
1106
- .join(',\n')}\n`;
1154
+ result += `\n${(select.columns || [ '*' ])
1155
+ .filter(col => !(select.mixin || Object.create(null))[firstPathStepId(col.ref)]) // No mixin columns
1156
+ .map(col => renderViewColumn(col, childEnv))
1157
+ .filter(s => s !== '')
1158
+ .join(',\n')}\n`;
1107
1159
  result += `${env.indent}FROM ${renderViewSource(artifactName, select.from, env)}`;
1108
1160
  if (select.where)
1109
1161
  result += `\n${env.indent}WHERE ${renderExpr(select.where, env, true, true)}`;
@@ -1290,7 +1342,8 @@ function toSqlDdl(csn, options) {
1290
1342
  function renderExpr(x, env, inline = true, nestedExpr = false) {
1291
1343
  // Compound expression
1292
1344
  if (Array.isArray(x)) {
1293
- return processExprArray(x, renderExpr, env, inline, nestedExpr);
1345
+ const tokens = x.map(item => renderExpr(item, env, inline, nestedExpr));
1346
+ return beautifyExprArray(tokens);
1294
1347
  }
1295
1348
  else if (typeof x === 'object' && x !== null) {
1296
1349
  if (nestedExpr && x.cast && x.cast.type)
@@ -1330,6 +1383,8 @@ function toSqlDdl(csn, options) {
1330
1383
  // Function call, possibly with args (use '=>' for named args)
1331
1384
  else if (x.func) {
1332
1385
  const funcName = smartFuncId(prepareIdentifier(x.func), options.toSql.dialect);
1386
+ if (x.xpr)
1387
+ return renderWindowFunction(funcName, x, env);
1333
1388
  return renderFunc(funcName, x, options.toSql.dialect, a => renderArgs(a, '=>', env, null));
1334
1389
  }
1335
1390
  // Nested expression
@@ -1352,29 +1407,36 @@ function toSqlDdl(csn, options) {
1352
1407
  throw new Error(`Unknown expression: ${JSON.stringify(x)}`);
1353
1408
  }
1354
1409
 
1410
+ function renderWindowFunction(funcName, node, env) {
1411
+ const suffix = node.xpr.shift(); // OVER
1412
+ let r = `${funcName}(${renderArgs(node, '=>', env, null)})`;
1413
+ r += ` ${suffix} (${renderExpr(node.xpr, env)})`;
1414
+ return r;
1415
+ }
1416
+
1355
1417
  function renderExpressionLiteral(x) {
1356
1418
  // Literal value, possibly with explicit 'literal' property
1357
1419
  switch (x.literal || typeof x.val) {
1358
1420
  case 'number':
1359
1421
  case 'boolean':
1360
1422
  case 'null':
1361
- // 17.42, NULL, TRUE
1423
+ // 17.42, NULL, TRUE
1362
1424
  return String(x.val).toUpperCase();
1363
1425
  case 'x':
1364
- // x'f000'
1426
+ // x'f000'
1365
1427
  return `${x.literal}'${x.val}'`;
1366
1428
  case 'date':
1367
1429
  case 'time':
1368
1430
  case 'timestamp':
1369
1431
  if (options.toSql.dialect === 'sqlite') {
1370
- // simple string literal '2017-11-02'
1432
+ // simple string literal '2017-11-02'
1371
1433
  return `'${x.val}'`;
1372
1434
  }
1373
1435
  // date'2017-11-02'
1374
1436
  return `${x.literal}'${x.val}'`;
1375
1437
 
1376
1438
  case 'string':
1377
- // 'foo', with proper escaping
1439
+ // 'foo', with proper escaping
1378
1440
  return `'${x.val.replace(/'/g, '\'\'')}'`;
1379
1441
  case 'object':
1380
1442
  if (x.val === null)
@@ -1388,7 +1450,12 @@ function toSqlDdl(csn, options) {
1388
1450
 
1389
1451
  function renderExpressionRef(x) {
1390
1452
  if (!x.param && !x.global) {
1453
+ const magicReplacement = getVariableReplacement(x.ref, options);
1454
+
1391
1455
  if (x.ref[0] === '$user') {
1456
+ if (magicReplacement !== null)
1457
+ return `'${magicReplacement}'`;
1458
+
1392
1459
  const result = render$user(x);
1393
1460
  // Invalid second path step doesn't cause a return
1394
1461
  if (result)
@@ -1400,6 +1467,9 @@ function toSqlDdl(csn, options) {
1400
1467
  if (result)
1401
1468
  return result;
1402
1469
  }
1470
+ else if (x.ref[0] === '$session' && magicReplacement !== null) {
1471
+ return `'${magicReplacement}'`;
1472
+ }
1403
1473
  }
1404
1474
  // FIXME: We currently cannot distinguish whether '$parameters' was quoted or not - we
1405
1475
  // assume that it was not if the path has length 2 (
@@ -1422,6 +1492,7 @@ function toSqlDdl(csn, options) {
1422
1492
  function render$user(x) {
1423
1493
  // FIXME: this is all not enough: we might need an explicit select item alias
1424
1494
  if (x.ref[1] === 'id') {
1495
+ // Keep the old-style for compatibilty with magicVars.id - instead of magicVars.user.id...
1425
1496
  if (options.toSql.user && typeof options.toSql.user === 'string' || options.toSql.user instanceof String)
1426
1497
  return `'${options.toSql.user}'`;
1427
1498
 
@@ -1441,7 +1512,7 @@ function toSqlDdl(csn, options) {
1441
1512
  }
1442
1513
  return 'SESSION_CONTEXT(\'LOCALE\')';
1443
1514
  }
1444
- // Basically: Second path step was invalid, do nothing
1515
+ // Basically: Second path step was invalid, do nothing - should not happen, see 'unknownMagic.js'
1445
1516
  return null;
1446
1517
  }
1447
1518
  /**
@@ -1539,13 +1610,13 @@ function toSqlDdl(csn, options) {
1539
1610
 
1540
1611
  // Not really a path step but an object-like function call
1541
1612
  if (s.func)
1542
- return `${s.func}(${renderArgs(s.args, '=>', env, null)})`;
1613
+ return `${s.func}(${renderArgs(s, '=>', env, null)})`;
1543
1614
 
1544
1615
  // Path step, possibly with view parameters and/or filters
1545
1616
  let result = `${quoteSqlId(s.id)}`;
1546
1617
  if (s.args) {
1547
1618
  // View parameters
1548
- result += `(${renderArgs(s.args, '=>', env, null)})`;
1619
+ result += `(${renderArgs(s, '=>', env, null)})`;
1549
1620
  }
1550
1621
  if (s.where) {
1551
1622
  // Filter, possibly with cardinality
@@ -33,7 +33,7 @@ const { implicitAs } = require('../../model/csnRefs');
33
33
  function renderFunc( funcName, node, dialect, renderArgs) {
34
34
  if (funcWithoutParen( node, dialect ))
35
35
  return funcName;
36
- return `${funcName}(${renderArgs( node.args )})`;
36
+ return `${funcName}(${renderArgs( node )})`;
37
37
  }
38
38
 
39
39
  /**
@@ -53,19 +53,14 @@ function funcWithoutParen( node, dialect ) {
53
53
  }
54
54
 
55
55
  /**
56
- * Process an expression array, rendering each subexpression and joining them appropriately
56
+ * Process already rendered expression parts by joining them nicely
57
57
  *
58
- * @param {Array} xpr Expression array
59
- * @param {Function} renderExpr Function to render expressions
60
- * @param {CdlRenderEnvironment} env Environment for rendering
61
- * @param {boolean} inline Wether to render the expression inline
62
- * @param {boolean} inExpr Wether to render as if a subexpression
58
+ * @param {Array} tokens Array of expression tokens
63
59
  *
64
60
  * @returns {string} The rendered xpr
65
61
  */
66
- function processExprArray(xpr, renderExpr, env, inline, inExpr) {
62
+ function beautifyExprArray(tokens) {
67
63
  // Simply concatenate array parts with spaces (with a tiny bit of beautification)
68
- const tokens = xpr.map(item => renderExpr(item, env, inline, inExpr));
69
64
  let result = '';
70
65
  for (let i = 0; i < tokens.length; i++) {
71
66
  result += tokens[i];
@@ -342,11 +337,10 @@ function addIntermediateContexts(csn, killList) {
342
337
  *
343
338
  * @param {CSN.Artifact} obj
344
339
  * @param {CSN.Options} options To check for `disableHanaComments`
345
- * @param {CSN.Artifact} artifact Artifact containing obj (in case of elem or columns)
346
340
  * @returns {boolean}
347
341
  */
348
- function hasHanaComment(obj, options, artifact = {}) {
349
- return !artifact['@cds.persistence.journal'] && !options.disableHanaComments && typeof obj.doc === 'string';
342
+ function hasHanaComment(obj, options) {
343
+ return !options.disableHanaComments && typeof obj.doc === 'string';
350
344
  }
351
345
  /**
352
346
  * Return the comment of the given artifact or element.
@@ -379,7 +373,7 @@ function getHanaComment(obj) {
379
373
 
380
374
  module.exports = {
381
375
  renderFunc,
382
- processExprArray,
376
+ beautifyExprArray,
383
377
  getNamespace,
384
378
  getRealName,
385
379
  addIntermediateContexts,
@@ -388,4 +382,5 @@ module.exports = {
388
382
  hasHanaComment,
389
383
  getHanaComment,
390
384
  findElement,
385
+ funcWithoutParen,
391
386
  };
@@ -20,9 +20,9 @@ const { smartId, delimitedId } = require('../../sql-identifier');
20
20
  function renderReferentialConstraint(constraint, indent, toUpperCase, csn, options, alterConstraint) {
21
21
  let quoteId;
22
22
  // for to.hana we can't utilize the sql identifier utils
23
- if (options.toHana) {
23
+ if (options.transformation === 'hdbcds') {
24
24
  quoteId = (id) => {
25
- if (options.toHana.names === 'plain')
25
+ if (options.sqlMapping === 'plain')
26
26
  return id.replace(/\./g, '_');
27
27
  return `"${id}"`;
28
28
  };
@@ -38,7 +38,7 @@ function renderReferentialConstraint(constraint, indent, toUpperCase, csn, optio
38
38
  constraint.parentTable = constraint.parentTable.toUpperCase();
39
39
  }
40
40
 
41
- const renderAsHdbconstraint = options.toHana ||
41
+ const renderAsHdbconstraint = options.transformation === 'hdbcds' ||
42
42
  (options.toSql && options.toSql.src === 'hdi') ||
43
43
  (options.manageConstraints && options.manageConstraints.src === 'hdi');
44
44
 
@@ -51,7 +51,12 @@ const sqlDialects = {
51
51
  effectiveName: name => name.toUpperCase(),
52
52
  asDelimitedId: name => `"${ name.replace(/"/g, '""')}"`,
53
53
  },
54
- // TODO: hdbcds for HANA CDS
54
+ hdbcds: {
55
+ regularRegex: /^[A-Za-z_][A-Za-z_0-9]*$/,
56
+ reservedWords: keywords.hdbcds,
57
+ effectiveName: name => name,
58
+ asDelimitedId: name => `"${ name.replace(/"/g, '""')}"`,
59
+ },
55
60
  };
56
61
 
57
62
  function smartId( name, dialect ) {
@@ -260,7 +260,7 @@ function processAssertUnique(csn, options, error, info) {
260
260
  * If the output format is SQL, the toSql renderer is responsible
261
261
  * to render the table constraints from the constraint dictionary.
262
262
  *
263
- * If options.toHana, no path flattening is done and association
263
+ * If options.transformation === 'hdbcds', no path flattening is done and association
264
264
  * paths are replaced with the foreign key paths by simply
265
265
  * concatenating the foreign key paths (available in element.keys).
266
266
  *
@@ -279,11 +279,10 @@ function rewriteUniqueConstraints(csn, options, pathDelimiter) {
279
279
  * @param {CSN.Artifact} artifact
280
280
  */
281
281
  function rewrite(artifact) {
282
- const naming = options.forHana.names || options.toSql.names;
283
282
  if (artifact.$tableConstraints && artifact.$tableConstraints.unique) {
284
283
  const uniqueConstraints = artifact.$tableConstraints.unique;
285
284
  // it's safe to add the tc here
286
- if (options.toHana) {
285
+ if (options.transformation === 'hdbcds') {
287
286
  if (!artifact.technicalConfig)
288
287
  artifact.technicalConfig = Object.create(null);
289
288
 
@@ -302,12 +301,12 @@ function rewriteUniqueConstraints(csn, options, pathDelimiter) {
302
301
  c.forEach((cpath) => {
303
302
  // If 'toSql' or 'toHana' and naming !== 'hdbcds'
304
303
  // concatenate path refs with appropriate delimiter
305
- if (!options.toHana || (options.toHana && naming !== 'hdbcds'))
304
+ if (options.transformation !== 'hdbcds' || (options.transformation === 'hdbcds' && options.sqlMapping !== 'hdbcds'))
306
305
  cpath.ref = [ cpath.ref.map(p => p.id).join( pathDelimiter ) ];
307
306
 
308
307
  // Foreign key substitution
309
308
  if (cpath._art.target) {
310
- if (!options.toHana || (options.toHana && naming !== 'hdbcds')) {
309
+ if (options.transformation !== 'hdbcds' || (options.transformation === 'hdbcds' && options.sqlMapping !== 'hdbcds')) {
311
310
  // read out new association and use $generatedFieldName
312
311
  // cpath._art still refers to the assoc definition
313
312
  // before the A2J transformation. This assoc
@@ -332,7 +331,7 @@ function rewriteUniqueConstraints(csn, options, pathDelimiter) {
332
331
  uniqueConstraints[uniqueConstraint] = rewrittenPaths;
333
332
 
334
333
  // now add the index for HANA CDS
335
- if (options.toHana) {
334
+ if (options.transformation === 'hdbcds') {
336
335
  const index = [ 'unique', 'index', { ref: [ uniqueConstraint ] }, 'on', '(' ];
337
336
  let i = 0;
338
337
  for (const constraint of rewrittenPaths) {