@sap/cds-compiler 2.10.2 → 2.11.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 (82) hide show
  1. package/CHANGELOG.md +90 -5
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +3 -1
  4. package/bin/cdsc.js +49 -25
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_BETA.md +10 -0
  8. package/lib/api/.eslintrc.json +2 -0
  9. package/lib/api/main.js +8 -36
  10. package/lib/api/options.js +15 -6
  11. package/lib/api/validate.js +30 -3
  12. package/lib/backends.js +12 -13
  13. package/lib/base/dictionaries.js +2 -1
  14. package/lib/base/keywords.js +3 -2
  15. package/lib/base/message-registry.js +34 -10
  16. package/lib/base/messages.js +38 -18
  17. package/lib/base/model.js +5 -4
  18. package/lib/base/optionProcessorHelper.js +57 -23
  19. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  20. package/lib/checks/selectItems.js +4 -0
  21. package/lib/checks/unknownMagic.js +6 -3
  22. package/lib/compiler/assert-consistency.js +9 -2
  23. package/lib/compiler/base.js +65 -0
  24. package/lib/compiler/builtins.js +62 -16
  25. package/lib/compiler/checks.js +2 -1
  26. package/lib/compiler/definer.js +66 -108
  27. package/lib/compiler/index.js +29 -29
  28. package/lib/compiler/propagator.js +5 -2
  29. package/lib/compiler/resolver.js +225 -58
  30. package/lib/compiler/shared.js +53 -229
  31. package/lib/compiler/utils.js +184 -0
  32. package/lib/edm/annotations/genericTranslation.js +1 -1
  33. package/lib/edm/csn2edm.js +3 -2
  34. package/lib/edm/edmPreprocessor.js +34 -38
  35. package/lib/edm/edmUtils.js +3 -3
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +17 -1
  38. package/lib/gen/language.tokens +79 -73
  39. package/lib/gen/languageLexer.interp +19 -1
  40. package/lib/gen/languageLexer.js +779 -731
  41. package/lib/gen/languageLexer.tokens +71 -65
  42. package/lib/gen/languageParser.js +4668 -4072
  43. package/lib/json/from-csn.js +10 -10
  44. package/lib/json/to-csn.js +228 -47
  45. package/lib/language/antlrParser.js +11 -0
  46. package/lib/language/errorStrategy.js +26 -8
  47. package/lib/language/genericAntlrParser.js +73 -14
  48. package/lib/language/language.g4 +79 -3
  49. package/lib/main.d.ts +215 -18
  50. package/lib/main.js +3 -1
  51. package/lib/model/api.js +2 -2
  52. package/lib/model/csnRefs.js +117 -33
  53. package/lib/model/csnUtils.js +65 -133
  54. package/lib/model/enrichCsn.js +62 -37
  55. package/lib/model/revealInternalProperties.js +25 -8
  56. package/lib/model/sortViews.js +8 -1
  57. package/lib/modelCompare/compare.js +2 -1
  58. package/lib/optionProcessor.js +33 -18
  59. package/lib/render/.eslintrc.json +1 -2
  60. package/lib/render/DuplicateChecker.js +1 -1
  61. package/lib/render/toCdl.js +15 -8
  62. package/lib/render/toHdbcds.js +26 -49
  63. package/lib/render/toSql.js +61 -39
  64. package/lib/render/utils/common.js +1 -1
  65. package/lib/transform/db/applyTransformations.js +189 -0
  66. package/lib/transform/db/constraints.js +273 -119
  67. package/lib/transform/db/draft.js +3 -2
  68. package/lib/transform/db/expansion.js +6 -4
  69. package/lib/transform/db/flattening.js +19 -3
  70. package/lib/transform/db/transformExists.js +102 -9
  71. package/lib/transform/db/views.js +485 -0
  72. package/lib/transform/forHanaNew.js +93 -448
  73. package/lib/transform/forOdataNew.js +9 -2
  74. package/lib/transform/localized.js +2 -0
  75. package/lib/transform/odata/structuralPath.js +1 -5
  76. package/lib/transform/transformUtilsNew.js +22 -8
  77. package/lib/transform/translateAssocsToJoins.js +7 -15
  78. package/lib/utils/file.js +11 -5
  79. package/lib/utils/term.js +65 -42
  80. package/lib/utils/timetrace.js +48 -26
  81. package/package.json +1 -1
  82. package/lib/transform/db/helpers.js +0 -58
package/CHANGELOG.md CHANGED
@@ -7,6 +7,95 @@
7
7
  Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
 
10
+ ## Version 2.11.4 - 2021-12-21
11
+
12
+ ### Fixed
13
+
14
+ - CDL parser: in many situations, improve message when people use reserved keywords as identifier
15
+ - Improve error text and error location for ambiguious auto-redirection target
16
+ - to.sql/hdi/hdbcds:
17
+ + Correctly detect `exists` in projections
18
+ + Correctly handle elements starting with `$` in the on-condition of associations
19
+ + Correctly handle sub queries in an entity defined with `projection on`
20
+ + Correctly handle associations in sub queries in a `from` of a sub query
21
+ + foreign key constraints: respect @assert.integrity: false for compositions
22
+ - to.hdbcds: Correctly quote elements named `$self` and `$projection`
23
+ - to.cdl: `when` was added to the keyword list for smart quoting
24
+ - Compiler support for code completion for `$user` and `$session` now respect user
25
+ provided variables in `options.variableReplacements`.
26
+ - API: `deduplicateMessages()` no longer removes messages for `duplicate` artifact/annotation errors.
27
+ Prior to this version, only one of the duplicated artifacts had a message, leaving the user to
28
+ guess where the other duplicates were.
29
+
30
+
31
+ ## Version 2.11.2 - 2021-12-06
32
+
33
+ ### Fixed
34
+
35
+ - to.sql/hdi/hdbcds:
36
+ + No foreign key constraint will be rendered for managed `composition of one` if annotated with `@assert.integrity: false`
37
+ + Correctly handle managed associations with other managed associations as foreign keys in conjunction with `exists`
38
+
39
+ ## Version 2.11.0 - 2021-12-02
40
+
41
+ ### Added
42
+
43
+ - Option `defaultBinaryLength` to set a `length` type facet for all definitions with type `cds.Binary`. This option
44
+ overrides the default binary length in the database backends and is also used as `MaxLength` attribute in Odata.
45
+ - If doc-comments are ignored by the compiler, an info message is now emitted. A doc-comment is ignored,
46
+ if it can't be assigned to an artifact. For example for two subsequent doc-comments, the first doc-comment
47
+ is ignored. To suppress these info messages, explicitly set option `docComment` to `false`.
48
+ - `cdsc`:
49
+ + `cdsc explain list` can now be used to get a list of message IDs with explanation texts.
50
+ + `cdsc` now respects the environment variable `NO_COLOR`. If set, no ANSI escape codes will be used.
51
+ Can be overwritten by `cdsc --color always`.
52
+ - to.sql/hdi: Support SQL Window Functions
53
+ - to.sql/hdi/hdbcds:
54
+ + Support configuration of `$session` and `$user` via option `variableReplacements`.
55
+ + Restricted support for SQL foreign key constraints if option `assertIntegrityType` is set to `"DB"`.
56
+ The behavior of this feature might change in the future.
57
+
58
+ ### Changed
59
+
60
+ - Updated OData vocabularies 'Common' and 'UI'.
61
+ - to.sql/hdi/hdbcds: The default length of `cds.Binary` is set to `5000` similar to `cds.String`.
62
+
63
+ ### Removed
64
+
65
+ - to.hdbcds: Doc comments on view columns are not rendered anymore. Doc comments on string literals will make the deployment fail
66
+ as the SAP HANA CDS compiler concatenates the doc comment with the string literal. Besides that, doc comments on view columns
67
+ are not transported to the database by SAP HANA CDS.
68
+ - to.hdbcds/sql/hdi: Forbid associations in filters after `exists` (except for nested `exists`), as the final behavior is not yet specified.
69
+
70
+ ### Fixed
71
+
72
+ - CSN parser: doc-comment extensions are no longer ignored.
73
+ - Properly check for duplicate annotation definitions.
74
+ - Correctly apply annotations on inherited enum symbols.
75
+ - Correctly apply annotations on elements in an inherited structure array.
76
+ - Fix a bug in API `defaultStringLength` value evaluation.
77
+ - Fix crash if named arguments are used in a function that's inside a `CASE` statement.
78
+ - to.sql/hdi/hdbcds:
79
+ + Properly flatten ad-hoc defined elements in `returns` / `params` of `actions` and `functions`.
80
+ + Correctly handle `*` in non-first position.
81
+ + Correctly handle action return types
82
+ + Correctly handle mixin association named `$self`
83
+ - to.cdl: doc-comments are no longer rendered twice.
84
+ - to.edm(x):
85
+ + Fix a bug in V2/V4 partner ship calculation.
86
+ + Remove warning of unknown types for Open Types in `@Core.Dictionary`.
87
+ + An empty CSN no longer results in a JavaScript type error
88
+
89
+ ## Version 2.10.4 - 2021-11-05
90
+
91
+ ### Fixed
92
+
93
+ - to.sql/hdi/hdbcds:
94
+ + Correctly complain about `exists` in conjunction with non-associations/compositions
95
+ + Don't resolve types in action returns, as this causes issues with $self-resolution
96
+
97
+ - to.edm(x): Be robust against transitively untyped keys in stacked view hierarchies
98
+
10
99
  ## Version 2.10.2 - 2021-10-29
11
100
 
12
101
  ### Fixed
@@ -38,13 +127,9 @@ The compiler behavior concerning `beta` features can change at any time without
38
127
  + `exists` can now be followed by more than one association step.
39
128
  `exists assoc.anotherassoc.moreassoc` is semantically equivalent to `exists assoc[exists anotherassoc[exists moreassoc]]`
40
129
 
41
- ### Removed
42
-
43
130
  ### Changed
44
131
 
45
- - to.odata: Inform when overwriting draft action annotations like @Common.DraftRoot.ActivationAction.
46
-
47
- ### Fixed
132
+ - to.odata: Inform when overwriting draft action annotations like `@Common.DraftRoot.ActivationAction`.
48
133
 
49
134
  ## Version 2.9.0 - 2021-10-15
50
135
 
@@ -11,7 +11,6 @@
11
11
  "no-process-exit": "off",
12
12
  "camelcase": "off",
13
13
  "radix": "off",
14
- // should probably be on
15
- "no-shadow": "off"
14
+ "no-shadow": "warn"
16
15
  }
17
16
  }
@@ -24,6 +24,7 @@
24
24
  'use strict';
25
25
 
26
26
  const parseLanguage = require('../lib/language/antlrParser');
27
+ const { createMessageFunctions } = require('../lib/base/messages');
27
28
 
28
29
  const fs = require('fs');
29
30
  const path = require('path');
@@ -51,10 +52,11 @@ process.exit(0); // success
51
52
 
52
53
  function modernizeIdentifierStyle(source, filename) {
53
54
  const options = { messages: [], attachTokens: true };
55
+ const messageFunctions = createMessageFunctions( options, 'parse', null );
54
56
 
55
57
  // parseLanguage does not throw on CompilationError, so
56
58
  // we do not need a try...catch block.
57
- const ast = parseLanguage(source, filename, options);
59
+ const ast = parseLanguage(source, filename, options, messageFunctions);
58
60
 
59
61
  // To avoid spam, only report errors.
60
62
  // Users should use the compiler to get all messages.
package/bin/cdsc.js CHANGED
@@ -26,8 +26,10 @@ const path = require('path');
26
26
  const { reveal } = require('../lib/model/revealInternalProperties');
27
27
  const enrichCsn = require('../lib/model/enrichCsn');
28
28
  const { optionProcessor } = require('../lib/optionProcessor');
29
- const { explainMessage, hasMessageExplanation, sortMessages } = require('../lib/base/messages');
30
- const term = require('../lib/utils/term');
29
+ const {
30
+ explainMessage, hasMessageExplanation, sortMessages, messageIdsWithExplanation,
31
+ } = require('../lib/base/messages');
32
+ const { term } = require('../lib/utils/term');
31
33
  const { splitLines } = require('../lib/utils/file');
32
34
  const { addLocalizationViews } = require('../lib/transform/localized');
33
35
  const { availableBetaFlags } = require('../lib/base/model');
@@ -52,9 +54,11 @@ function remapCmdOptions(options, cmdOptions) {
52
54
  options.sqlMapping = value;
53
55
  break;
54
56
  case 'user':
55
- if (!options.magicVars)
56
- options.magicVars = {};
57
- options.magicVars.user = value;
57
+ if (!options.variableReplacements)
58
+ options.variableReplacements = {};
59
+ if (!options.variableReplacements.$user)
60
+ options.variableReplacements.$user = {};
61
+ options.variableReplacements.$user.id = value;
58
62
  break;
59
63
  case 'dialect':
60
64
  options.sqlDialect = value;
@@ -63,9 +67,11 @@ function remapCmdOptions(options, cmdOptions) {
63
67
  options.odataVersion = value;
64
68
  break;
65
69
  case 'locale':
66
- if (!options.magicVars)
67
- options.magicVars = {};
68
- options.magicVars.locale = value;
70
+ if (!options.variableReplacements)
71
+ options.variableReplacements = {};
72
+ if (!options.variableReplacements.$user)
73
+ options.variableReplacements.$user = {};
74
+ options.variableReplacements.$user.locale = value;
69
75
  break;
70
76
  default:
71
77
  options[key] = value;
@@ -124,8 +130,6 @@ try {
124
130
  global.cds = {};
125
131
  global.cds.home = cmdLine.options.cdsHome;
126
132
  }
127
- // Default color mode is 'auto'
128
- term.useColor(cmdLine.options.color || 'auto');
129
133
 
130
134
  // Set default command if required
131
135
  cmdLine.command = cmdLine.command || 'toCsn';
@@ -137,10 +141,7 @@ try {
137
141
  if (cmdLine.command === 'parseCdl') {
138
142
  cmdLine.command = 'toCsn';
139
143
  cmdLine.options.parseCdl = true;
140
- if (cmdLine.args.files.length > 1) {
141
- const err = `'parseCdl' expects exactly one file! ${cmdLine.args.files.length} provided.`;
142
- displayUsage(err, optionProcessor.commands.parseCdl.helpText, 2);
143
- }
144
+ cmdLine.args.files = [ cmdLine.args.file ];
144
145
  }
145
146
 
146
147
  if (cmdLine.options.directBackend)
@@ -155,6 +156,21 @@ try {
155
156
  });
156
157
  }
157
158
 
159
+ // remap string values for `assertIntegrity` option to boolean
160
+ if (cmdLine.options.assertIntegrity &&
161
+ cmdLine.options.assertIntegrity === 'true' ||
162
+ cmdLine.options.assertIntegrity === 'false'
163
+ )
164
+ cmdLine.options.assertIntegrity = cmdLine.options.assertIntegrity === 'true';
165
+
166
+ // remap string values for `constraintsAsAlter` option to boolean
167
+ if (cmdLine.options.constraintsAsAlter &&
168
+ cmdLine.options.constraintsAsAlter === 'true' ||
169
+ cmdLine.options.constraintsAsAlter === 'false'
170
+ )
171
+ cmdLine.options.constraintsAsAlter = cmdLine.options.constraintsAsAlter === 'true';
172
+
173
+
158
174
  // Enable all beta-flags if betaMode is set to true
159
175
  if (cmdLine.options.betaMode)
160
176
  cmdLine.options.beta = availableBetaFlags;
@@ -231,6 +247,8 @@ function executeCommandLine(command, options, args) {
231
247
  if (options.out && options.out !== '-' && !fs.existsSync(options.out))
232
248
  fs.mkdirSync(options.out);
233
249
 
250
+ // Default color mode is 'auto'
251
+ const colorTerm = term(options.color || 'auto');
234
252
 
235
253
  // Add implementation functions corresponding to commands here
236
254
  const commands = {
@@ -393,14 +411,15 @@ function executeCommandLine(command, options, args) {
393
411
  }
394
412
 
395
413
  function explain() {
396
- if (args.length !== 1)
397
- displayUsage('Command \'explain\' expects exactly one message-id.', optionProcessor.commands.explain.helpText, 2);
414
+ if (args.messageId === 'list') {
415
+ console.log(messageIdsWithExplanation().join('\n'));
416
+ throw new ProcessExitError(0);
417
+ }
398
418
 
399
- const id = args.files[0];
400
- if (!hasMessageExplanation(id))
401
- console.error(`Message '${id}' does not have an explanation!`);
419
+ if (!hasMessageExplanation(args.messageId))
420
+ console.error(`Message '${args.messageId}' does not have an explanation!`);
402
421
  else
403
- console.log(explainMessage(id));
422
+ console.log(explainMessage(args.messageId));
404
423
  }
405
424
 
406
425
  // Display error messages in `err` resulting from a compilation. Also set
@@ -464,16 +483,20 @@ function executeCommandLine(command, options, args) {
464
483
  hasAtLeastOneExplanation = hasAtLeastOneExplanation || main.hasMessageExplanation(msg.messageId);
465
484
  const name = msg.location && msg.location.file;
466
485
  const fullFilePath = name ? path.resolve('', name) : undefined;
467
- const context = fullFilePath && sourceLines(fullFilePath);
486
+ const context = fullFilePath ? sourceLines(fullFilePath) : [];
468
487
  log(main.messageStringMultiline(msg, {
469
- normalizeFilename, noMessageId: !options.showMessageId, withLineSpacer: true, hintExplanation: true,
488
+ normalizeFilename,
489
+ noMessageId: !options.showMessageId,
490
+ withLineSpacer: true,
491
+ hintExplanation: true,
492
+ color: options.color,
470
493
  }));
471
494
  if (context)
472
- log(main.messageContext(context, msg));
495
+ log(main.messageContext(context, msg, { color: options.color }));
473
496
  log(); // newline
474
497
  });
475
498
  if (options.showMessageId && hasAtLeastOneExplanation)
476
- log(`${term.help('help')}: Messages marked with '…' have an explanation text. Use \`cdsc explain <message-id>\` for a more detailed error description.`);
499
+ log(`${colorTerm.help('help')}: Messages marked with '…' have an explanation text. Use \`cdsc explain <message-id>\` for a more detailed error description.`);
477
500
  }
478
501
  return model;
479
502
  }
@@ -549,10 +572,11 @@ function executeCommandLine(command, options, args) {
549
572
  }
550
573
 
551
574
  function catchErrors(err) {
575
+ // @ts-ignore
552
576
  if (err instanceof Error && err.hasBeenReported)
553
577
  return;
554
578
  console.error( '' );
555
- console.error( 'INTERNAL ERROR: %s', err );
579
+ console.error( 'INTERNAL ERROR:' );
556
580
  console.error( util.inspect(err, false, null) );
557
581
  console.error( '' );
558
582
  process.exitCode = 70;
package/bin/cdsse.js CHANGED
@@ -11,6 +11,7 @@
11
11
  // Planned are: gotoDefinition, highlight (for syntax highighting).
12
12
 
13
13
  /* eslint no-console:off */
14
+ // @ts-nocheck
14
15
 
15
16
  'use strict';
16
17
 
package/bin/cdsv2m.js CHANGED
@@ -34,10 +34,11 @@ function usage( err ) {
34
34
  function ria() {
35
35
  const annotates = Object.create( null );
36
36
  const msgs = options.messages.filter( m => m.messageId === 'redirected-implicitly-ambiguous' );
37
- // regex match on message text not for productive code!
37
+ // 'Choose via $(ANNO) one of $(SORTED_ARTS) as redirection target for $(TARGET) in … $(ART) otherwise'
38
+ // NOTE: regex match on message text not for productive code!
38
39
  for (const msgObj of msgs) {
39
40
  const matches = msgObj.message.match( /["“][^"”]+["”]/g );
40
- matches.slice(2).forEach( (name) => {
41
+ matches.slice( 1, -2 ).forEach( (name) => {
41
42
  annotates[name.slice( 1, -1 )] = true;
42
43
  } );
43
44
  }
@@ -8,6 +8,16 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
  **Don't use `beta` fixes, changes and features in productive mode.**
10
10
 
11
+ ## Version 2.11.0
12
+
13
+ ### Removed `foreignKeyConstraints`
14
+
15
+ ## Version 2.10.4
16
+
17
+ ### Fixed `nestedProjections`
18
+
19
+ - to.sql/hdi/hdbcds: Correctly handle a `*` at the not-first place in the query
20
+
11
21
  ## Version 2.6.0
12
22
 
13
23
  ### Removed `pretransformedCSN`
@@ -14,6 +14,8 @@
14
14
  "jsdoc"
15
15
  ],
16
16
  "rules": {
17
+ // Does not recognize TS types
18
+ "jsdoc/no-undefined-types": "off",
17
19
  // eslint-plugin-jsdoc warning
18
20
  "jsdoc/require-property": 0,
19
21
  // =airbnb, >eslint:
package/lib/api/main.js CHANGED
@@ -14,7 +14,7 @@ const { toSqlDdl } = require('../render/toSql');
14
14
  const { compareModels } = require('../modelCompare/compare');
15
15
  const sortViews = require('../model/sortViews');
16
16
  const { getResultingName } = require('../model/csnUtils');
17
- const timetrace = require('../utils/timetrace');
17
+ const { timetrace } = require('../utils/timetrace');
18
18
  const { transformForHanaWithCsn } = require('../transform/forHanaNew');
19
19
 
20
20
  /**
@@ -135,7 +135,7 @@ function odataInternal(csn, internalOptions) {
135
135
  * Return a odata-transformed CSN
136
136
  *
137
137
  * @param {CSN.Model} csn Clean input CSN
138
- * @param {oDataOptions} [options={}] Options
138
+ * @param {ODataOptions} [options={}] Options
139
139
  * @returns {oDataCSN} Return an oData-pre-processed CSN
140
140
  */
141
141
  function odata(csn, options = {}) {
@@ -159,7 +159,7 @@ function cdl(csn, externalOptions = {}) {
159
159
  * Transform a CSN like to.sql
160
160
  *
161
161
  * @param {CSN.Model} csn Plain input CSN
162
- * @param {sqlOptions} [options={}] Options
162
+ * @param {SqlOptions} [options={}] Options
163
163
  * @returns {CSN.Model} CSN transformed like to.sql
164
164
  * @private
165
165
  */
@@ -202,7 +202,7 @@ function forHdbcds(csn, options = {}) {
202
202
  * Process the given CSN into SQL.
203
203
  *
204
204
  * @param {CSN.Model} csn A clean input CSN
205
- * @param {sqlOptions} [options={}] Options
205
+ * @param {SqlOptions} [options={}] Options
206
206
  * @returns {SQL[]} Array of SQL statements, tables first, views second
207
207
  */
208
208
  function sql(csn, options = {}) {
@@ -454,7 +454,7 @@ function hdbcds(csn, options = {}) {
454
454
  * Generate a edm document for the given service
455
455
  *
456
456
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
457
- * @param {oDataOptions} [options={}] Options
457
+ * @param {ODataOptions} [options={}] Options
458
458
  * @returns {edm} The JSON representation of the service
459
459
  */
460
460
  function edm(csn, options = {}) {
@@ -484,7 +484,7 @@ edm.all = edmall;
484
484
  * Generate edm documents for all services
485
485
  *
486
486
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
487
- * @param {oDataOptions} [options={}] Options
487
+ * @param {ODataOptions} [options={}] Options
488
488
  * @returns {edms} { <service>:<JSON representation>, ...}
489
489
  */
490
490
  function edmall(csn, options = {}) {
@@ -516,7 +516,7 @@ function edmall(csn, options = {}) {
516
516
  * Generate a edmx document for the given service
517
517
  *
518
518
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
519
- * @param {oDataOptions} [options={}] Options
519
+ * @param {ODataOptions} [options={}] Options
520
520
  * @returns {edmx} The XML representation of the service
521
521
  */
522
522
  function edmx(csn, options = {}) {
@@ -547,7 +547,7 @@ edmx.all = edmxall;
547
547
  * Generate edmx documents for all services
548
548
  *
549
549
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
550
- * @param {oDataOptions} [options={}] Options
550
+ * @param {ODataOptions} [options={}] Options
551
551
  * @returns {edmxs} { <service>:<XML representation>, ...}
552
552
  */
553
553
  function edmxall(csn, options = {}) {
@@ -709,20 +709,6 @@ function publishCsnProcessor( processor, _name ) {
709
709
  * @property {Array} [messages] Allows collecting all messages in the options instead of printing them to stderr.
710
710
  */
711
711
 
712
- /**
713
- * Options available for all oData-based functions
714
- *
715
- * @typedef {object} oDataOptions
716
- * @property {object} [beta] Enable experimental features - not for productive use!
717
- * @property {boolean} [longAutoexposed=false] Deprecated: Produce long names (with underscores) for autoexposed entities
718
- * @property {Map<string, number>} [severities={}] Map of message-id and severity that allows setting the severity for the given message
719
- * @property {Array} [messages] Allows collecting all messages in the options instead of printing them to stderr.
720
- * @property {oDataVersion} [odataVersion='v4'] Odata version to use
721
- * @property {oDataFormat} [odataFormat='flat'] Wether to generate oData as flat or as structured. Structured only with v4.
722
- * @property {NamingMode} [sqlMapping='plain'] Naming mode to use
723
- * @property {string} [service] If a single service is to be rendered
724
- */
725
-
726
712
  /**
727
713
  * Options available for to.hdi
728
714
  *
@@ -747,20 +733,6 @@ function publishCsnProcessor( processor, _name ) {
747
733
  * @property {Array} [messages] Allows collecting all messages in the options instead of printing them to stderr.
748
734
  */
749
735
 
750
- /**
751
- * Options available for to.sql
752
- *
753
- * @typedef {object} sqlOptions
754
- * @property {NamingMode} [sqlMapping='plain'] Naming mode to use
755
- * @property {SQLDialect} [sqlDialect='sqlite'] SQL dialect to use
756
- * @property {object} [magicVars] Object containing values for magic variables like "$user"
757
- * @property {string} [magicVars.locale] Value for the "$user.locale" in "sqlite" dialect
758
- * @property {string} [magicVars.user] Value for the "$user" variable in "sqlite" dialect
759
- * @property {object} [beta] Enable experimental features - not for productive use!
760
- * @property {boolean} [longAutoexposed=false] Deprecated: Produce long names (with underscores) for autoexposed entities
761
- * @property {Map<string, number>} [severities={}] Map of message-id and severity that allows setting the severity for the given message
762
- * @property {Array} [messages] Allows collecting all messages in the options instead of printing them to stderr.
763
- */
764
736
 
765
737
  /**
766
738
  * A fresh (just compiled, not transformed) CSN
@@ -15,6 +15,7 @@ const publicOptionsNewAPI = [
15
15
  'severities',
16
16
  'messages',
17
17
  'withLocations',
18
+ 'defaultBinaryLength',
18
19
  'defaultStringLength',
19
20
  'csnFlavor',
20
21
  // DB
@@ -23,7 +24,8 @@ const publicOptionsNewAPI = [
23
24
  'sqlChangeMode',
24
25
  'allowCsnDowngrade',
25
26
  'joinfk',
26
- 'magicVars',
27
+ 'magicVars', // deprecated
28
+ 'variableReplacements',
27
29
  // ODATA
28
30
  'odataVersion',
29
31
  'odataFormat',
@@ -47,9 +49,11 @@ const privateOptions = [
47
49
  'traceParserAmb',
48
50
  'testMode',
49
51
  'testSortCsn',
50
- 'constraintsNotEnforced',
51
- 'constraintsNotValidated',
52
- 'skipDbConstraints',
52
+ 'constraintsAsAlter',
53
+ 'integrityNotEnforced',
54
+ 'integrityNotValidated',
55
+ 'assertIntegrity',
56
+ 'assertIntegrityType',
53
57
  'noRecompile',
54
58
  'internalMsg',
55
59
  'disableHanaComments', // in case of issues with hana comment rendering
@@ -109,6 +113,10 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
109
113
  mapToOldNames(optionName, optionValue);
110
114
  }
111
115
 
116
+ // Convenience for $user -> $user.id replacement
117
+ if (options.variableReplacements && options.variableReplacements.$user && typeof options.variableReplacements.$user === 'string')
118
+ options.variableReplacements.$user = { id: options.variableReplacements.$user };
119
+
112
120
  /**
113
121
  * Map a new-style option to it's old format
114
122
  *
@@ -130,6 +138,7 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
130
138
  case 'sqlMapping':
131
139
  options.names = optionValue;
132
140
  break;
141
+ // No need to remap variableReplacements here - we use the new mechanism with that directly
133
142
  case 'magicVars':
134
143
  if (optionValue.user)
135
144
  options.user = optionValue.user;
@@ -149,7 +158,7 @@ module.exports = {
149
158
  sql: (options) => {
150
159
  const hardOptions = { src: 'sql' };
151
160
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'plain' };
152
- const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ], 'to.sql');
161
+ const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming', 'constraints-as-alter-sqlite' ], 'to.sql');
153
162
 
154
163
  const result = Object.assign({}, processed);
155
164
  result.toSql = Object.assign({}, processed);
@@ -157,7 +166,7 @@ module.exports = {
157
166
  return result;
158
167
  },
159
168
  hdi: (options) => {
160
- const hardOptions = { src: 'hdi' };
169
+ const hardOptions = { src: 'hdi', constraintsAsAlter: false };
161
170
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
162
171
  const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdi');
163
172
 
@@ -25,13 +25,14 @@ const booleanValidator = {
25
25
  /**
26
26
  * Generate a Validator that validates that the
27
27
  * input is a string and one of the available options.
28
+ * The validation of the option values is case-insensitive.
28
29
  *
29
30
  * @param {any} availableValues Available values
30
31
  * @returns {Validator} Return a validator for a string in an expected range
31
32
  */
32
33
  function generateStringValidator(availableValues) {
33
34
  return {
34
- validate: val => typeof val === 'string' && availableValues.indexOf(val) !== -1,
35
+ validate: val => typeof val === 'string' && availableValues.some( av => av.toLowerCase() === val.toLowerCase() ),
35
36
  expected: (val) => {
36
37
  return typeof val !== 'string' ? 'type string' : availableValues.join(', ');
37
38
  },
@@ -70,6 +71,14 @@ const validators = {
70
71
  return val === null ? val : `type ${ typeof val }`;
71
72
  },
72
73
  },
74
+ // TODO: Maybe do a deep validation of the whole object with leafs?
75
+ variableReplacements: {
76
+ validate: val => val !== null && typeof val === 'object' && !Array.isArray(val),
77
+ expected: () => 'type object',
78
+ found: (val) => {
79
+ return val === null ? val : `type ${ typeof val }`;
80
+ },
81
+ },
73
82
  messages: {
74
83
  validate: val => Array.isArray(val),
75
84
  expected: () => 'type array',
@@ -89,10 +98,17 @@ const validators = {
89
98
  expected: () => 'type array of string',
90
99
  found: val => `type ${ typeof val }`,
91
100
  },
101
+ defaultBinaryLength: {
102
+ validate: val => !Number.isNaN(Number(val)) && Number.isInteger(Number.parseFloat(val)),
103
+
104
+ expected: () => 'Integer literal',
105
+ found: val => `${ (!Number.isNaN(Number(val)) ? val : 'Not a Number') }`,
106
+ },
92
107
  defaultStringLength: {
93
- validate: val => Number.isInteger(val),
108
+ validate: val => !Number.isNaN(Number(val)) && Number.isInteger(Number.parseFloat(val)),
109
+
94
110
  expected: () => 'Integer literal',
95
- found: val => `type ${ typeof val }`,
111
+ found: val => `${ (!Number.isNaN(Number(val)) ? val : 'Not a Number') }`,
96
112
  },
97
113
  csnFlavor: {
98
114
  validate: val => typeof val === 'string',
@@ -102,6 +118,12 @@ const validators = {
102
118
  dictionaryPrototype: {
103
119
  validate: () => true,
104
120
  },
121
+ assertIntegrity: {
122
+ validate: val => typeof val === 'string' && val === 'individual' || typeof val === 'boolean',
123
+ expected: () => 'a boolean or a string with value \'individual\'',
124
+ found: val => (typeof val === 'string' ? val : `type ${ typeof val }`),
125
+ },
126
+ assertIntegrityType: generateStringValidator([ 'DB', 'RT' ]),
105
127
  };
106
128
 
107
129
  const allCombinationValidators = {
@@ -120,6 +142,11 @@ const allCombinationValidators = {
120
142
  severity: 'warning',
121
143
  getMessage: () => 'Option "beta" was used. This option should not be used in productive scenarios!',
122
144
  },
145
+ 'constraints-as-alter-sqlite': {
146
+ validate: options => options.constraintsAsAlter && options.sqlDialect && options.sqlDialect === 'sqlite',
147
+ severity: 'warning',
148
+ getMessage: options => `Option 'constraintsAsAlter' is ignored for sqlDialect '${ options.sqlDialect }'`,
149
+ },
123
150
  };
124
151
  /* eslint-disable jsdoc/no-undefined-types */
125
152
  /**