@sap/cds-compiler 2.10.4 → 2.12.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 (103) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +10 -8
  4. package/bin/cdsc.js +58 -35
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  8. package/doc/CHANGELOG_BETA.md +16 -0
  9. package/lib/api/.eslintrc.json +2 -0
  10. package/lib/api/main.js +10 -36
  11. package/lib/api/options.js +17 -8
  12. package/lib/api/validate.js +30 -3
  13. package/lib/backends.js +12 -13
  14. package/lib/base/dictionaries.js +2 -1
  15. package/lib/base/keywords.js +3 -2
  16. package/lib/base/message-registry.js +64 -11
  17. package/lib/base/messages.js +38 -18
  18. package/lib/base/model.js +6 -4
  19. package/lib/base/optionProcessorHelper.js +148 -86
  20. package/lib/checks/.eslintrc.json +2 -0
  21. package/lib/checks/actionsFunctions.js +2 -1
  22. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  23. package/lib/checks/foreignKeys.js +4 -4
  24. package/lib/checks/managedInType.js +4 -4
  25. package/lib/checks/queryNoDbArtifacts.js +1 -3
  26. package/lib/checks/selectItems.js +4 -0
  27. package/lib/checks/sql-snippets.js +93 -0
  28. package/lib/checks/unknownMagic.js +6 -3
  29. package/lib/checks/validator.js +8 -0
  30. package/lib/compiler/assert-consistency.js +14 -5
  31. package/lib/compiler/base.js +64 -0
  32. package/lib/compiler/builtins.js +62 -16
  33. package/lib/compiler/checks.js +34 -10
  34. package/lib/compiler/definer.js +91 -112
  35. package/lib/compiler/index.js +30 -30
  36. package/lib/compiler/propagator.js +8 -4
  37. package/lib/compiler/resolver.js +279 -63
  38. package/lib/compiler/shared.js +65 -230
  39. package/lib/compiler/utils.js +191 -0
  40. package/lib/edm/annotations/genericTranslation.js +35 -18
  41. package/lib/edm/annotations/preprocessAnnotations.js +1 -1
  42. package/lib/edm/csn2edm.js +4 -3
  43. package/lib/edm/edm.js +8 -8
  44. package/lib/edm/edmPreprocessor.js +61 -59
  45. package/lib/edm/edmUtils.js +14 -15
  46. package/lib/gen/Dictionary.json +82 -40
  47. package/lib/gen/language.checksum +1 -1
  48. package/lib/gen/language.interp +19 -1
  49. package/lib/gen/language.tokens +80 -73
  50. package/lib/gen/languageLexer.interp +27 -1
  51. package/lib/gen/languageLexer.js +925 -826
  52. package/lib/gen/languageLexer.tokens +72 -65
  53. package/lib/gen/languageParser.js +4817 -4102
  54. package/lib/json/from-csn.js +57 -26
  55. package/lib/json/to-csn.js +244 -51
  56. package/lib/language/antlrParser.js +12 -1
  57. package/lib/language/docCommentParser.js +1 -1
  58. package/lib/language/errorStrategy.js +26 -8
  59. package/lib/language/genericAntlrParser.js +106 -30
  60. package/lib/language/language.g4 +200 -70
  61. package/lib/language/multiLineStringParser.js +536 -0
  62. package/lib/main.d.ts +220 -21
  63. package/lib/main.js +6 -3
  64. package/lib/model/api.js +2 -2
  65. package/lib/model/csnRefs.js +218 -86
  66. package/lib/model/csnUtils.js +99 -178
  67. package/lib/model/enrichCsn.js +84 -43
  68. package/lib/model/revealInternalProperties.js +25 -8
  69. package/lib/model/sortViews.js +8 -1
  70. package/lib/modelCompare/compare.js +2 -1
  71. package/lib/optionProcessor.js +33 -18
  72. package/lib/render/.eslintrc.json +1 -2
  73. package/lib/render/DuplicateChecker.js +2 -2
  74. package/lib/render/manageConstraints.js +1 -1
  75. package/lib/render/toCdl.js +202 -82
  76. package/lib/render/toHdbcds.js +194 -135
  77. package/lib/render/toRename.js +7 -10
  78. package/lib/render/toSql.js +91 -51
  79. package/lib/render/utils/common.js +24 -5
  80. package/lib/render/utils/sql.js +6 -4
  81. package/lib/transform/braceExpression.js +4 -2
  82. package/lib/transform/db/applyTransformations.js +189 -0
  83. package/lib/transform/db/associations.js +389 -0
  84. package/lib/transform/db/cdsPersistence.js +150 -0
  85. package/lib/transform/db/constraints.js +275 -119
  86. package/lib/transform/db/draft.js +6 -4
  87. package/lib/transform/db/expansion.js +10 -9
  88. package/lib/transform/db/flattening.js +23 -8
  89. package/lib/transform/db/temporal.js +236 -0
  90. package/lib/transform/db/transformExists.js +106 -25
  91. package/lib/transform/db/views.js +485 -0
  92. package/lib/transform/forHanaNew.js +90 -1036
  93. package/lib/transform/forOdataNew.js +11 -3
  94. package/lib/transform/localized.js +5 -14
  95. package/lib/transform/odata/generateForeignKeyElements.js +2 -2
  96. package/lib/transform/transformUtilsNew.js +34 -20
  97. package/lib/transform/translateAssocsToJoins.js +15 -23
  98. package/lib/transform/universalCsnEnricher.js +217 -47
  99. package/lib/utils/file.js +13 -6
  100. package/lib/utils/term.js +65 -42
  101. package/lib/utils/timetrace.js +55 -27
  102. package/package.json +1 -1
  103. package/lib/transform/db/helpers.js +0 -58
@@ -4,10 +4,11 @@
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
10
  renderFunc, beautifyExprArray, cdsToSqlTypes, getHanaComment, hasHanaComment,
11
+ getSqlSnippets,
11
12
  } = require('./utils/common');
12
13
  const {
13
14
  renderReferentialConstraint, getIdentifierUtils,
@@ -15,10 +16,11 @@ const {
15
16
  const DuplicateChecker = require('./DuplicateChecker');
16
17
  const { checkCSNVersion } = require('../json/csnVersion');
17
18
  const { makeMessageFunction } = require('../base/messages');
18
- const timetrace = require('../utils/timetrace');
19
+ const { timetrace } = require('../utils/timetrace');
19
20
  const { isBetaEnabled, isDeprecatedEnabled } = require('../base/model');
20
21
  const { smartFuncId } = require('../sql-identifier');
21
22
  const { sortCsn } = require('../json/to-csn');
23
+ const { manageConstraints } = require('./manageConstraints');
22
24
 
23
25
 
24
26
  /**
@@ -185,10 +187,10 @@ function toSqlDdl(csn, options) {
185
187
  checkCSNVersion(csn, options);
186
188
 
187
189
  // The final result in hdb-kind-specific form, without leading CREATE, without trailing newlines
188
- // (note that the order here is relevant for transmission into 'resultObj.sql' below and that
190
+ // (note that the order here is relevant for transmission into 'mainResultObj.sql' below and that
189
191
  // the attribute names must be the HDI plugin names for --src hdi)
190
192
  // The result object may have a `sql` dictionary for `toSql`.
191
- const resultObj = {
193
+ const mainResultObj = {
192
194
  hdbtabletype: Object.create(null),
193
195
  hdbtable: Object.create(null),
194
196
  hdbindex: Object.create(null),
@@ -214,13 +216,12 @@ function toSqlDdl(csn, options) {
214
216
  // Current indentation string
215
217
  indent: '',
216
218
  };
217
- renderArtifactInto(artifactName, artifact, resultObj, env);
219
+ renderArtifactInto(artifactName, artifact, mainResultObj, env);
218
220
  });
219
221
 
220
222
  // Render each deleted artifact
221
223
  for (const artifactName in csn.deletions)
222
- renderArtifactDeletionInto(artifactName, csn.deletions[artifactName], resultObj);
223
-
224
+ renderArtifactDeletionInto(artifactName, csn.deletions[artifactName], mainResultObj);
224
225
 
225
226
  // Render each artifact extension
226
227
  // Only HANA SQL is currently supported.
@@ -231,7 +232,7 @@ function toSqlDdl(csn, options) {
231
232
  const artifactName = extension.extend;
232
233
  const _artifact = csn.definitions[artifactName];
233
234
  const env = { indent: '', _artifact };
234
- renderArtifactExtensionInto(artifactName, _artifact, extension, resultObj, env);
235
+ renderArtifactExtensionInto(artifactName, _artifact, extension, mainResultObj, env);
235
236
  }
236
237
  }
237
238
  }
@@ -244,7 +245,7 @@ function toSqlDdl(csn, options) {
244
245
  const artifactName = migration.migrate;
245
246
  const _artifact = csn.definitions[artifactName];
246
247
  const env = { indent: '', _artifact };
247
- renderArtifactMigrationInto(artifactName, migration, resultObj, env);
248
+ renderArtifactMigrationInto(artifactName, migration, mainResultObj, env);
248
249
  }
249
250
  }
250
251
  }
@@ -265,34 +266,42 @@ function toSqlDdl(csn, options) {
265
266
 
266
267
  // Handle hdbKinds separately from alterTable case
267
268
  // eslint-disable-next-line no-unused-vars
268
- const { deletions, migrations, ...hdbKinds } = resultObj;
269
+ const { deletions, migrations: _, ...hdbKinds } = mainResultObj;
269
270
  for (const hdbKind of Object.keys(hdbKinds)) {
270
- for (const name in resultObj[hdbKind]) {
271
+ for (const name in mainResultObj[hdbKind]) {
271
272
  if (options.toSql.src === 'sql') {
272
- let sourceString = resultObj[hdbKind][name];
273
+ let sourceString = mainResultObj[hdbKind][name];
273
274
  // Hack: Other than in 'hdbtable' files, in HANA SQL COLUMN is not mandatory but default.
274
275
  if (options.toSql.dialect === 'hana' && hdbKind === 'hdbtable' && sourceString.startsWith('COLUMN '))
275
276
  sourceString = sourceString.slice('COLUMN '.length);
276
-
277
277
  sql[name] = `${options.testMode ? '' : sqlVersionLine}CREATE ${sourceString};`;
278
278
  }
279
279
  else if (!options.testMode) {
280
- resultObj[hdbKind][name] = sqlVersionLine + resultObj[hdbKind][name];
280
+ mainResultObj[hdbKind][name] = sqlVersionLine + mainResultObj[hdbKind][name];
281
281
  }
282
282
  }
283
283
  if (options.toSql.src === 'sql')
284
- delete resultObj[hdbKind];
284
+ delete mainResultObj[hdbKind];
285
+ }
286
+
287
+ // add `ALTER TABLE ADD CONSTRAINT` statements if requested
288
+ if (options.sqlDialect !== 'sqlite' && options.constraintsAsAlter) {
289
+ const alterStmts = manageConstraints(csn, options);
290
+
291
+ for ( const constraintName of Object.keys(alterStmts))
292
+ sql[constraintName] = `${options.testMode ? '' : sqlVersionLine}${alterStmts[constraintName]}`;
293
+ mainResultObj.sql = sql;
285
294
  }
286
295
 
287
296
  if (options.toSql.src === 'sql')
288
- resultObj.sql = sql;
297
+ mainResultObj.sql = sql;
289
298
 
290
299
  for (const name in deletions)
291
300
  deletions[name] = `${options.testMode ? '' : sqlVersionLine}${deletions[name]}`;
292
301
 
293
302
 
294
303
  timetrace.stop();
295
- return resultObj;
304
+ return mainResultObj;
296
305
 
297
306
  /**
298
307
  * Render an artifact into the appropriate dictionary of 'resultObj'.
@@ -483,7 +492,9 @@ function toSqlDdl(csn, options) {
483
492
  env._artifact = art;
484
493
  const childEnv = increaseIndent(env);
485
494
  const hanaTc = art.technicalConfig && art.technicalConfig.hana;
486
- let result = '';
495
+ // tables can have @sql.prepend and @sql.append
496
+ const { front, back } = getSqlSnippets(options, art);
497
+ let result = front;
487
498
  // Only HANA has row/column tables
488
499
  if (options.toSql.dialect === 'hana') {
489
500
  if (hanaTc && hanaTc.storeType) {
@@ -500,13 +511,9 @@ function toSqlDdl(csn, options) {
500
511
  result += `TABLE ${tableName}`;
501
512
  result += ' (\n';
502
513
  const elements = Object.keys(art.elements).map(eltName => renderElement(artifactName, eltName, art.elements[eltName], definitionsDuplicateChecker, getFzIndex(eltName, hanaTc), childEnv)).filter(s => s !== '').join(',\n');
503
- if (elements !== '') {
514
+ if (elements !== '')
504
515
  result += elements;
505
- }
506
- else {
507
- // TODO: Already be handled by 'empty-entity' reclassification; better location
508
- error(null, [ 'definitions', artifactName ], 'Entities must have at least one element that is non-virtual');
509
- }
516
+
510
517
  const uniqueFields = Object.keys(art.elements).filter(name => art.elements[name].unique && !art.elements[name].virtual)
511
518
  .map(name => quoteSqlId(name))
512
519
  .join(', ');
@@ -517,7 +524,8 @@ function toSqlDdl(csn, options) {
517
524
  if (primaryKeys !== '')
518
525
  result += `,\n${childEnv.indent}${primaryKeys}`;
519
526
 
520
- if (art.$tableConstraints && art.$tableConstraints.referential) {
527
+ const constraintsAsAlter = options.constraintsAsAlter && options.sqlDialect !== 'sqlite';
528
+ if ( !constraintsAsAlter && art.$tableConstraints && art.$tableConstraints.referential) {
521
529
  const renderReferentialConstraintsAsHdbconstraint = options.toSql.src === 'hdi';
522
530
  const referentialConstraints = {};
523
531
  Object.entries(art.$tableConstraints.referential)
@@ -567,9 +575,12 @@ function toSqlDdl(csn, options) {
567
575
  if (options.toSql.dialect === 'hana')
568
576
  renderIndexesInto(art.technicalConfig && art.technicalConfig.hana.indexes, artifactName, resultObj, env);
569
577
 
570
- if (options.toSql.dialect === 'hana' && hasHanaComment(art, options, art))
578
+ if (options.toSql.dialect === 'hana' && hasHanaComment(art, options))
571
579
  result += ` COMMENT '${getHanaComment(art)}'`;
572
580
 
581
+ if (back)
582
+ result += back;
583
+
573
584
  resultObj.hdbtable[artifactName] = result;
574
585
  }
575
586
 
@@ -605,12 +616,12 @@ function toSqlDdl(csn, options) {
605
616
  if (!(artifactName in resultObj.migrations))
606
617
  resultObj.migrations[artifactName] = [];
607
618
 
608
- const migrations = sqlArray.map(sql => ({ drop, sql }));
619
+ const migrations = sqlArray.map(migrationSql => ({ drop, sql: migrationSql }));
609
620
  resultObj.migrations[artifactName].push(...migrations);
610
621
  }
611
622
 
612
- function addDeletion(resultObj, artifactName, sql) {
613
- resultObj.deletions[artifactName] = sql;
623
+ function addDeletion(resultObj, artifactName, deletionSql) {
624
+ resultObj.deletions[artifactName] = deletionSql;
614
625
  }
615
626
 
616
627
  /**
@@ -667,9 +678,15 @@ function toSqlDdl(csn, options) {
667
678
  if (fzindex && options.toSql.dialect === 'hana')
668
679
  result += ` ${renderExpr(fzindex, env)}`;
669
680
 
670
- if (options.toSql.dialect === 'hana' && hasHanaComment(elm, options, env._artifact))
681
+ if (options.toSql.dialect === 'hana' && hasHanaComment(elm, options))
671
682
  result += ` COMMENT '${getHanaComment(elm)}'`;
672
683
 
684
+ // (table) elements can only have a @sql.append
685
+ const { back } = getSqlSnippets(options, elm);
686
+
687
+ if (back !== '')
688
+ result += back;
689
+
673
690
  return result;
674
691
  }
675
692
 
@@ -979,7 +996,7 @@ function toSqlDdl(csn, options) {
979
996
  * @param {object} node with `args` to render
980
997
  * @param {string} sep Separator between args
981
998
  * @param {object} env Render environment
982
- * @param {string} syntax Some magic A2J paramter - for calcview parameter rendering
999
+ * @param {string|null} syntax Some magic A2J parameter - for calcview parameter rendering
983
1000
  * @returns {string} Rendered arguments
984
1001
  * @throws Throws if args is not an array or object.
985
1002
  */
@@ -1002,14 +1019,13 @@ function toSqlDdl(csn, options) {
1002
1019
  * Render the given argument/parameter correctly.
1003
1020
  *
1004
1021
  * @param {string} arg Argument to render
1005
- * @param {string} syntax Some magic A2J paramter - for calcview parameter rendering
1022
+ * @param {string|null} parameterSyntax Some magic A2J parameter - for calcview parameter rendering
1006
1023
  * @returns {string} Rendered argument
1007
1024
  */
1008
- function decorateParameter(arg, syntax) {
1009
- if (syntax === 'calcview')
1025
+ function decorateParameter(arg, parameterSyntax) {
1026
+ if (parameterSyntax === 'calcview')
1010
1027
  return `PLACEHOLDER."$$${arg}$$"`;
1011
1028
 
1012
-
1013
1029
  return quoteSqlId(arg);
1014
1030
  }
1015
1031
  }
@@ -1054,7 +1070,7 @@ function toSqlDdl(csn, options) {
1054
1070
  definitionsDuplicateChecker.addArtifact(art['@cds.persistence.name'], art && art.$location, artifactName);
1055
1071
  let result = `VIEW ${viewName}`;
1056
1072
 
1057
- if (options.toSql.dialect === 'hana' && hasHanaComment(art, options, art))
1073
+ if (options.toSql.dialect === 'hana' && hasHanaComment(art, options))
1058
1074
  result += ` COMMENT '${getHanaComment(art)}'`;
1059
1075
 
1060
1076
  result += renderParameterDefinitions(artifactName, art.params);
@@ -1070,6 +1086,11 @@ function toSqlDdl(csn, options) {
1070
1086
  result += `${env.indent})`;
1071
1087
  }
1072
1088
 
1089
+ // views can only have a @sql.append
1090
+ const { back } = getSqlSnippets(options, art);
1091
+ if (back)
1092
+ result += back;
1093
+
1073
1094
  return result;
1074
1095
  }
1075
1096
 
@@ -1077,7 +1098,7 @@ function toSqlDdl(csn, options) {
1077
1098
  * Render the parameter definition of a view if any. Return the parameters in parentheses, or an empty string
1078
1099
  *
1079
1100
  * @param {string} artifactName Name of the view
1080
- * @param {Array} params Array of parameters
1101
+ * @param {Object} params Dictionary of parameters
1081
1102
  * @returns {string} Rendered parameters
1082
1103
  */
1083
1104
  function renderParameterDefinitions(artifactName, params) {
@@ -1087,7 +1108,7 @@ function toSqlDdl(csn, options) {
1087
1108
  for (const pn in params) {
1088
1109
  const p = params[pn];
1089
1110
  if (p.notNull === true || p.notNull === false)
1090
- info(null, [ 'definitions', artifactName, 'params', pn ], 'Not Null constraints on SQL view parameters are not allowed and are ignored');
1111
+ info('query-ignoring-param-nullability', [ 'definitions', artifactName, 'params', pn ], { '#': 'sql' });
1091
1112
  // do not quote parameter identifiers for naming mode "quoted" / "hdbcds"
1092
1113
  // this would be an incompatible change, as non-uppercased, quoted identifiers
1093
1114
  // are rejected by the HANA compiler.
@@ -1333,27 +1354,27 @@ function toSqlDdl(csn, options) {
1333
1354
  * (no trailing LF, don't indent if inline)
1334
1355
  *
1335
1356
  * @todo Reuse this with toCdl
1336
- * @param {Array|object|string} x Expression to render
1357
+ * @param {Array|object|string} expr Expression to render
1337
1358
  * @param {object} env Render environment
1338
1359
  * @param {boolean} inline Wether to render the expression inline
1339
1360
  * @param {boolean} nestedExpr Wether to treat the expression as nested
1340
1361
  * @returns {string} Rendered expression
1341
1362
  */
1342
- function renderExpr(x, env, inline = true, nestedExpr = false) {
1363
+ function renderExpr(expr, env, inline = true, nestedExpr = false) {
1343
1364
  // Compound expression
1344
- if (Array.isArray(x)) {
1345
- const tokens = x.map(item => renderExpr(item, env, inline, nestedExpr));
1365
+ if (Array.isArray(expr)) {
1366
+ const tokens = expr.map(item => renderExpr(item, env, inline, nestedExpr));
1346
1367
  return beautifyExprArray(tokens);
1347
1368
  }
1348
- else if (typeof x === 'object' && x !== null) {
1349
- if (nestedExpr && x.cast && x.cast.type)
1350
- return renderExplicitTypeCast(renderExprObject());
1351
- return renderExprObject();
1369
+ else if (typeof expr === 'object' && expr !== null) {
1370
+ if (nestedExpr && expr.cast && expr.cast.type)
1371
+ return renderExplicitTypeCast(expr, renderExprObject(expr));
1372
+ return renderExprObject(expr);
1352
1373
  }
1353
1374
  // Not a literal value but part of an operator, function etc - just leave as it is
1354
1375
  // FIXME: For the sake of simplicity, we should get away from all this uppercasing in toSql
1355
1376
 
1356
- return String(x).toUpperCase();
1377
+ return String(expr).toUpperCase();
1357
1378
 
1358
1379
 
1359
1380
  /**
@@ -1361,7 +1382,7 @@ function toSqlDdl(csn, options) {
1361
1382
  *
1362
1383
  * @returns {string} String representation of the expression
1363
1384
  */
1364
- function renderExprObject() {
1385
+ function renderExprObject(x) {
1365
1386
  if (x.list) {
1366
1387
  return `(${x.list.map(item => renderExpr(item)).join(', ')})`;
1367
1388
  }
@@ -1383,6 +1404,8 @@ function toSqlDdl(csn, options) {
1383
1404
  // Function call, possibly with args (use '=>' for named args)
1384
1405
  else if (x.func) {
1385
1406
  const funcName = smartFuncId(prepareIdentifier(x.func), options.toSql.dialect);
1407
+ if (x.xpr)
1408
+ return renderWindowFunction(funcName, x, env);
1386
1409
  return renderFunc(funcName, x, options.toSql.dialect, a => renderArgs(a, '=>', env, null));
1387
1410
  }
1388
1411
  // Nested expression
@@ -1405,6 +1428,13 @@ function toSqlDdl(csn, options) {
1405
1428
  throw new Error(`Unknown expression: ${JSON.stringify(x)}`);
1406
1429
  }
1407
1430
 
1431
+ function renderWindowFunction(funcName, node, fctEnv) {
1432
+ const suffix = node.xpr[0]; // OVER
1433
+ let r = `${funcName}(${renderArgs(node, '=>', fctEnv, null)})`;
1434
+ r += ` ${suffix} (${renderExpr(node.xpr.slice(1), fctEnv)})`; // do not pass suffix in renderExpr
1435
+ return r;
1436
+ }
1437
+
1408
1438
  function renderExpressionLiteral(x) {
1409
1439
  // Literal value, possibly with explicit 'literal' property
1410
1440
  switch (x.literal || typeof x.val) {
@@ -1441,7 +1471,12 @@ function toSqlDdl(csn, options) {
1441
1471
 
1442
1472
  function renderExpressionRef(x) {
1443
1473
  if (!x.param && !x.global) {
1474
+ const magicReplacement = getVariableReplacement(x.ref, options);
1475
+
1444
1476
  if (x.ref[0] === '$user') {
1477
+ if (magicReplacement !== null)
1478
+ return `'${magicReplacement}'`;
1479
+
1445
1480
  const result = render$user(x);
1446
1481
  // Invalid second path step doesn't cause a return
1447
1482
  if (result)
@@ -1453,6 +1488,9 @@ function toSqlDdl(csn, options) {
1453
1488
  if (result)
1454
1489
  return result;
1455
1490
  }
1491
+ else if (x.ref[0] === '$session' && magicReplacement !== null) {
1492
+ return `'${magicReplacement}'`;
1493
+ }
1456
1494
  }
1457
1495
  // FIXME: We currently cannot distinguish whether '$parameters' was quoted or not - we
1458
1496
  // assume that it was not if the path has length 2 (
@@ -1475,6 +1513,7 @@ function toSqlDdl(csn, options) {
1475
1513
  function render$user(x) {
1476
1514
  // FIXME: this is all not enough: we might need an explicit select item alias
1477
1515
  if (x.ref[1] === 'id') {
1516
+ // Keep the old-style for compatibilty with magicVars.id - instead of magicVars.user.id...
1478
1517
  if (options.toSql.user && typeof options.toSql.user === 'string' || options.toSql.user instanceof String)
1479
1518
  return `'${options.toSql.user}'`;
1480
1519
 
@@ -1482,7 +1521,7 @@ function toSqlDdl(csn, options) {
1482
1521
  return `'${options.toSql.user.id}'`;
1483
1522
 
1484
1523
  if (options.toSql.dialect === 'sqlite' || options.toSql.dialect === 'plain') {
1485
- warning(null, null, 'The "$user" variable is not supported. Use the "toSql.user" option to set a value for "$user.id"');
1524
+ warning(null, null, 'The "$user" variable is not supported. Use option "variableReplacements" to specify a value for "$user.id"');
1486
1525
  return '\'$user.id\'';
1487
1526
  }
1488
1527
  return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
@@ -1494,7 +1533,7 @@ function toSqlDdl(csn, options) {
1494
1533
  }
1495
1534
  return 'SESSION_CONTEXT(\'LOCALE\')';
1496
1535
  }
1497
- // Basically: Second path step was invalid, do nothing
1536
+ // Basically: Second path step was invalid, do nothing - should not happen, see 'unknownMagic.js'
1498
1537
  return null;
1499
1538
  }
1500
1539
  /**
@@ -1547,10 +1586,11 @@ function toSqlDdl(csn, options) {
1547
1586
  /**
1548
1587
  * Renders an explicit `cast()` inside an 'xpr'.
1549
1588
  *
1589
+ * @param {object} x Expression with cast
1550
1590
  * @param {string} value Value to cast
1551
1591
  * @returns {string} CAST statement
1552
1592
  */
1553
- function renderExplicitTypeCast(value) {
1593
+ function renderExplicitTypeCast(x, value) {
1554
1594
  const typeRef = renderBuiltinType(x.cast.type) + renderTypeParameters(x.cast);
1555
1595
  return `CAST(${value} AS ${typeRef})`;
1556
1596
  }
@@ -19,6 +19,7 @@ const {
19
19
  } = require('../../model/csnUtils');
20
20
 
21
21
  const { implicitAs } = require('../../model/csnRefs');
22
+ const { isBetaEnabled } = require('../../base/model');
22
23
 
23
24
 
24
25
  /**
@@ -205,12 +206,11 @@ function addMissingChildContexts(csn, artifactName, killList) {
205
206
  addPossibleGaps(name.slice(artifactName.length + 1).split('.'), artifactName);
206
207
  }
207
208
 
208
- function addPossibleGaps(possibleGaps, artifactName) {
209
- let possibleGap = artifactName;
209
+ function addPossibleGaps(possibleGaps, gapArtifactName) {
210
210
  for (const gap of possibleGaps) {
211
- possibleGap += `.${gap}`;
212
- if (!csn.definitions[possibleGap]) {
213
- const contextName = possibleGap;
211
+ gapArtifactName += `.${gap}`;
212
+ if (!csn.definitions[gapArtifactName]) {
213
+ const contextName = gapArtifactName;
214
214
  csn.definitions[contextName] = {
215
215
  kind: 'context',
216
216
  };
@@ -355,6 +355,24 @@ function getHanaComment(obj) {
355
355
  return obj.doc.split('\n\n')[0].trim().replace(/'/g, "''");
356
356
  }
357
357
 
358
+ /**
359
+ * Get the @sql.prepend/append if set - already add a space after/before.
360
+ * If no value is set, use '';
361
+ *
362
+ * @param {CSN.Options} options
363
+ * @param {object} obj
364
+ * @returns {object} object with .front and .back
365
+ */
366
+ function getSqlSnippets(options, obj) {
367
+ if (isBetaEnabled(options, 'sqlSnippets')) {
368
+ const front = obj['@sql.prepend'] ? `${obj['@sql.prepend']} ` : '';
369
+ const back = obj['@sql.append'] ? ` ${obj['@sql.append']}` : '';
370
+
371
+ return { front, back };
372
+ }
373
+ return { front: '', back: '' };
374
+ }
375
+
358
376
  /**
359
377
  * @typedef CdlRenderEnvironment Rendering environment used throughout the render process.
360
378
  *
@@ -383,4 +401,5 @@ module.exports = {
383
401
  getHanaComment,
384
402
  findElement,
385
403
  funcWithoutParen,
404
+ getSqlSnippets,
386
405
  };
@@ -13,11 +13,11 @@ const { smartId, delimitedId } = require('../../sql-identifier');
13
13
  * @param {boolean} toUpperCase Wether to uppercase the identifier
14
14
  * @param {CSN.Model} csn CSN
15
15
  * @param {CSN.Options} options is needed for the naming mode and the sql dialect
16
- * @param {boolean} alterConstraint whether the constraint should be rendered as part of an ALTER TABLE statement
16
+ * @param {boolean} [alterConstraint=false] whether the constraint should be rendered as part of an ALTER TABLE statement
17
17
  *
18
18
  * @returns {string} SQL statement which can be used to create the referential constraint on the db.
19
19
  */
20
- function renderReferentialConstraint(constraint, indent, toUpperCase, csn, options, alterConstraint) {
20
+ function renderReferentialConstraint(constraint, indent, toUpperCase, csn, options, alterConstraint = false) {
21
21
  let quoteId;
22
22
  // for to.hana we can't utilize the sql identifier utils
23
23
  if (options.transformation === 'hdbcds') {
@@ -51,14 +51,16 @@ function renderReferentialConstraint(constraint, indent, toUpperCase, csn, optio
51
51
  if (!alterConstraint) {
52
52
  result += `${indent}FOREIGN KEY(${constraint.foreignKey.map(quoteId).join(', ')})\n`;
53
53
  result += `${indent}REFERENCES ${quoteId(getResultingName(csn, names, constraint.parentTable))}(${constraint.parentKey.map(quoteId).join(', ')})\n`;
54
+ const onDeleteRemark = constraint.onDeleteRemark ? ` -- ${constraint.onDeleteRemark}` : '';
55
+
54
56
  // omit 'RESTRICT' action for ON UPDATE / ON DELETE, because it interferes with deferred constraint check
55
57
  if (forSqlite) {
56
58
  if (constraint.onDelete === 'CASCADE' )
57
- result += `${indent}ON DELETE ${constraint.onDelete}${constraint.onDeleteRemark ? ` -- ${constraint.onDeleteRemark}` : ''}\n`;
59
+ result += `${indent}ON DELETE ${constraint.onDelete}${onDeleteRemark}\n`;
58
60
  }
59
61
  else {
60
62
  result += `${indent}ON UPDATE RESTRICT\n`;
61
- result += `${indent}ON DELETE ${constraint.onDelete}${constraint.onDeleteRemark ? ` -- ${constraint.onDeleteRemark}` : ''}\n`;
63
+ result += `${indent}ON DELETE ${constraint.onDelete}${onDeleteRemark}\n`;
62
64
  }
63
65
  }
64
66
  // constraint enforcement / validation must be switched off using sqlite pragma statement
@@ -1,8 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  function isAlreadyBraced(expression, start, end){
4
- const isBraced = start - 1 > -1 && end + 1 < expression.length && expression[start-1] === '(' && expression[end+1] === ')';
5
- return isBraced;
4
+ return start - 1 > -1 &&
5
+ end + 1 < expression.length &&
6
+ expression[start-1] === '(' &&
7
+ expression[end+1] === ')';
6
8
  }
7
9
 
8
10
  function binarycomparison(expression, token, index){