@sap/cds-compiler 4.5.0 → 4.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +50 -7
  2. package/bin/cdsc.js +13 -11
  3. package/doc/CHANGELOG_BETA.md +6 -0
  4. package/lib/api/main.js +256 -115
  5. package/lib/api/options.js +8 -0
  6. package/lib/base/message-registry.js +17 -4
  7. package/lib/base/messages.js +15 -3
  8. package/lib/base/model.js +1 -0
  9. package/lib/base/optionProcessorHelper.js +45 -176
  10. package/lib/checks/elements.js +32 -34
  11. package/lib/checks/enricher.js +39 -3
  12. package/lib/checks/validator.js +2 -3
  13. package/lib/compiler/assert-consistency.js +2 -1
  14. package/lib/compiler/builtins.js +20 -4
  15. package/lib/compiler/checks.js +30 -6
  16. package/lib/compiler/define.js +31 -9
  17. package/lib/compiler/populate.js +5 -1
  18. package/lib/compiler/resolve.js +26 -21
  19. package/lib/compiler/shared.js +19 -9
  20. package/lib/compiler/tweak-assocs.js +82 -107
  21. package/lib/compiler/utils.js +2 -1
  22. package/lib/edm/annotations/edmJson.js +23 -22
  23. package/lib/edm/annotations/genericTranslation.js +14 -4
  24. package/lib/edm/csn2edm.js +24 -10
  25. package/lib/edm/edmInboundChecks.js +1 -2
  26. package/lib/edm/edmPreprocessor.js +11 -9
  27. package/lib/edm/edmUtils.js +5 -2
  28. package/lib/gen/Dictionary.json +3 -1
  29. package/lib/gen/language.checksum +1 -1
  30. package/lib/gen/language.interp +4 -1
  31. package/lib/gen/language.tokens +1 -0
  32. package/lib/gen/languageParser.js +5253 -5214
  33. package/lib/json/to-csn.js +7 -1
  34. package/lib/language/antlrParser.js +19 -1
  35. package/lib/language/errorStrategy.js +21 -4
  36. package/lib/language/genericAntlrParser.js +9 -11
  37. package/lib/main.d.ts +28 -3
  38. package/lib/main.js +3 -0
  39. package/lib/model/csnRefs.js +4 -1
  40. package/lib/model/csnUtils.js +12 -7
  41. package/lib/optionProcessor.js +21 -19
  42. package/lib/render/manageConstraints.js +13 -29
  43. package/lib/render/toCdl.js +18 -15
  44. package/lib/render/toHdbcds.js +59 -28
  45. package/lib/render/toRename.js +6 -10
  46. package/lib/render/toSql.js +57 -82
  47. package/lib/render/utils/common.js +17 -0
  48. package/lib/transform/.eslintrc.json +9 -1
  49. package/lib/transform/addTenantFields.js +228 -0
  50. package/lib/transform/db/applyTransformations.js +27 -31
  51. package/lib/transform/db/assertUnique.js +4 -4
  52. package/lib/transform/db/cdsPersistence.js +1 -1
  53. package/lib/transform/db/flattening.js +68 -69
  54. package/lib/transform/db/temporal.js +1 -1
  55. package/lib/transform/draft/db.js +2 -16
  56. package/lib/transform/draft/odata.js +3 -3
  57. package/lib/transform/effective/associations.js +3 -5
  58. package/lib/transform/effective/main.js +6 -9
  59. package/lib/transform/forOdata.js +13 -9
  60. package/lib/transform/forRelationalDB.js +36 -17
  61. package/lib/transform/odata/toFinalBaseType.js +3 -3
  62. package/lib/transform/odata/typesExposure.js +14 -5
  63. package/lib/transform/transformUtils.js +47 -34
  64. package/lib/transform/translateAssocsToJoins.js +33 -8
  65. package/package.json +2 -2
@@ -206,6 +206,10 @@ const typeProperties = [
206
206
  'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
207
207
  '$typeArgs', // for unresolved type arguments, e.g. through parseCql
208
208
  ];
209
+ // Properties which cause a `cast` property to be rendered
210
+ const castProperties = [
211
+ 'target', 'enum', 'items', 'type',
212
+ ];
209
213
 
210
214
  const csnDictionaries = [
211
215
  'args', 'params', 'enum', 'mixin', 'elements', 'actions', 'definitions',
@@ -1495,7 +1499,9 @@ function addElementAsColumn( elem, cols ) {
1495
1499
  // elements of sub queries (in expr) are hidden (not set via Object.assign):
1496
1500
  if (!expr.cast && expr.elements)
1497
1501
  setHidden( col, 'elements', expr.elements );
1498
- if (elem.type && !elem.type.$inferred || elem.target && !elem.target.$inferred)
1502
+ // CDL-style cast with explicit type properties
1503
+ if (castProperties.findIndex(prop => (elem[prop] &&
1504
+ !elem[prop].$inferred && !elem[prop][$inferred])) > -1)
1499
1505
  cast( col, elem );
1500
1506
  }
1501
1507
  finally {
@@ -78,9 +78,11 @@ class RewriteTypeTokenStream extends antlr4.CommonTokenStream {
78
78
  function initTokenRewrite( recognizer, ts ) { // ts = tokenStream
79
79
  ts.DOTbeforeBRACE = Parser.DOTbeforeBRACE;
80
80
  ts.BRACE = tokenTypeOf( recognizer, "'{'" );
81
+ ts.BRACE_CLOSE = tokenTypeOf( recognizer, "'}'" );
81
82
  ts.DOT = tokenTypeOf( recognizer, "'.'" );
82
83
  ts.ASTERISK = tokenTypeOf( recognizer, "'*'" );
83
84
  ts.AT = tokenTypeOf( recognizer, "'@'" );
85
+ ts.SEMICOLON = tokenTypeOf( recognizer, "';'" );
84
86
  ts.NEW = Parser.NEW;
85
87
  ts.Identifier = Parser.Identifier;
86
88
  ts.PAREN = tokenTypeOf( recognizer, "'('" );
@@ -90,7 +92,22 @@ function initTokenRewrite( recognizer, ts ) { // ts = tokenStream
90
92
  if (ts.DOT && ts.DOTbeforeBRACE)
91
93
  recognizer.tokenRewrite[ts.DOTbeforeBRACE - Parser.Identifier] = ts.DOT;
92
94
  }
93
- // console.log( ts.DOTbeforeBRACE, ts.BRACE, ts.DOT, recognizer.tokenRewrite );
95
+ }
96
+
97
+ function initCodeCompletionTokenArrays( parser ) {
98
+ // Set of top-level keywords used for code completion after token '}'
99
+ // belonging to a top-level definition
100
+ const startRuleIndex = parser.ruleNames.indexOf('start');
101
+ const startState = parser.atn.ruleToStartState[startRuleIndex].stateNumber;
102
+ const tokens = parser.atn.nextTokens(parser.atn.states[startState]);
103
+ tokens.removeOne(parser.symbolicNames.indexOf('NAMESPACE'));
104
+ tokens.removeOne(parser.symbolicNames.indexOf('HideAlternatives'));
105
+
106
+ parser.topLevelKeywords = [];
107
+ for (const interval of tokens.intervals) {
108
+ for (let i = interval.start; i < interval.stop; i++)
109
+ parser.topLevelKeywords.push(i);
110
+ }
94
111
  }
95
112
 
96
113
  function tokenTypeOf( recognizer, literalName ) {
@@ -123,6 +140,7 @@ function parse( source, filename = '<undefined>.cds',
123
140
  parser.$messageFunctions = messageFunctions;
124
141
 
125
142
  initTokenRewrite( parser, tokenStream );
143
+ initCodeCompletionTokenArrays(parser);
126
144
  // comment the following 2 lines if you want to output the parser errors directly:
127
145
  parser.messageErrorListener = errorListener;
128
146
  parser._errHandler = new errorStrategy.KeywordErrorStrategy();
@@ -447,16 +447,33 @@ function intervalSetToArray( recognizer, expected, excludesForNextToken ) {
447
447
  for (let j = v.start; j < v.stop; j++) {
448
448
  // a generic keyword as such does not appear in messages, only its replacements,
449
449
  // which are function name and argument position dependent:
450
- if (j === pc.GenericExpr)
450
+ if (j === pc.GenericExpr) {
451
451
  names.push( ...recognizer.$genericKeywords.expr );
452
- else if (j === pc.GenericSeparator)
452
+ }
453
+ else if (j === pc.GenericSeparator) {
453
454
  names.push( ...recognizer.$genericKeywords.separator );
454
- else if (j === pc.GenericIntro)
455
+ }
456
+ else if (j === pc.GenericIntro) {
455
457
  names.push( ...recognizer.$genericKeywords.introMsg );
458
+ }
459
+ else if (j === pc.SemicolonTopLevel) {
460
+ // We only insert a semikolon (i.e. make it optional) after a closing brace.
461
+ // If the previous token is not `}`, don't propose these keywords, as ';' is required.
462
+ if (recognizer._input.LA(-1) === recognizer._input.BRACE_CLOSE) {
463
+ const name = recognizer.topLevelKeywords.map(i => expected
464
+ .elementName(recognizer.literalNames, recognizer.symbolicNames, i));
465
+ names.push(...name);
466
+ if (recognizer._ctx.outer?.kind !== 'source') {
467
+ if (names.includes('<EOF>'))
468
+ names.splice(names.indexOf('<EOF>'), 1);
469
+ }
470
+ }
471
+ }
456
472
  // other expected tokens usually appear in messages, except the helper tokens
457
473
  // which are used to solve ambiguities via the parser method setLocalToken():
458
- else if (j !== pc.HelperToken1 && j !== pc.HelperToken2)
474
+ else if (j !== pc.HelperToken1 && j !== pc.HelperToken2) {
459
475
  names.push( expected.elementName(recognizer.literalNames, recognizer.symbolicNames, j ) );
476
+ }
460
477
  }
461
478
  }
462
479
  // The parser method excludeExpected() additionally removes some tokens from the message:
@@ -1247,30 +1247,28 @@ function finalizeDictOrArray( dict ) {
1247
1247
  }
1248
1248
 
1249
1249
  function insertSemicolon() {
1250
- // hand-selected keyword list: first() set of `artifactDefOrExtend`
1251
- const allowedKeywords = [ this.constructor.ACTION, this.constructor.ABSTRACT,
1252
- this.constructor.ANNOTATE, this.constructor.ANNOTATION, this.constructor.ASPECT,
1253
- this.constructor.CONTEXT, this.constructor.DEFINE, this.constructor.ENTITY,
1254
- this.constructor.EOF, this.constructor.EVENT, this.constructor.EXTEND,
1255
- this.constructor.FUNCTION, this.constructor.SERVICE,
1256
- this.constructor.TYPE, this.constructor.VIEW, this.literalNames.indexOf( "'@'" ) ];
1257
-
1258
1250
  const currentToken = this._input.tokens[this._input.index];
1251
+ const requireSemicolon = this.topLevelKeywords.includes(currentToken.type);
1259
1252
 
1260
- if (allowedKeywords.includes(currentToken.type)) {
1253
+ if (requireSemicolon) {
1254
+ this.noAssignmentInSameLine();
1261
1255
  const prev = this._input.LT(-1);
1262
1256
  const t = CommonTokenFactory.create(
1263
- currentToken.source, this.literalNames.indexOf( "';'" ),
1264
- '', currentToken.channel,
1257
+ currentToken.source,
1258
+ this.literalNames.indexOf( "';'" ),
1259
+ '', antlr4.Token.DEFAULT_CHANNEL,
1265
1260
  prev.stop, prev.stop,
1266
1261
  prev.line, prev.column
1267
1262
  );
1263
+
1268
1264
  t.tokenIndex = prev.tokenIndex + 1;
1265
+
1269
1266
  this._input.tokens.splice(t.tokenIndex, 0, t);
1270
1267
 
1271
1268
  // Update tokenIndex: There could have been comments between two non-hidden tokens.
1272
1269
  for (let tokenIndex = t.tokenIndex + 1; tokenIndex < this._input.tokens.length; tokenIndex++)
1273
1270
  this._input.tokens[tokenIndex].tokenIndex += 1;
1271
+
1274
1272
  this._input.index = t.tokenIndex;
1275
1273
  }
1276
1274
  }
package/lib/main.d.ts CHANGED
@@ -27,9 +27,9 @@ declare namespace compiler {
27
27
  messages?: object[]
28
28
  /**
29
29
  * Dictionary of message-ids and their reclassified severity. This option
30
- * can be used to increase the severity of messages. The compiler will
31
- * ignore decreased severities as this may lead to issues during
32
- * compilation otherwise.
30
+ * can be used to increase the severity of messages. The compiler may
31
+ * ignore decreased severities of error messages as this may lead to issues
32
+ * during compilation otherwise.
33
33
  */
34
34
  severities?: { [messageId: string]: MessageSeverity}
35
35
  /**
@@ -1023,6 +1023,31 @@ declare namespace compiler {
1023
1023
  function migration(csn: CSN, options: SqlOptions, beforeImage: CSN): SqlMigrationResult;
1024
1024
  }
1025
1025
 
1026
+ /**
1027
+ * Renders the given CSN into EDM in the JSON format _and_ XML format.
1028
+ * That is, it is a combination of `to.edm()` and `to.edmx()`.
1029
+ * Requires `options.service` to be set.
1030
+ *
1031
+ * Not to be confused with `for.odata()`, which returns an OData transformed CSN.
1032
+ *
1033
+ * @returns An object `'<protocol>': object` where the value is `'<serviceName>': object` entry
1034
+ * which consists of `{edmx: string, edm?: object}`.
1035
+ * @since v4.6.0
1036
+ */
1037
+ function odata(csn: CSN, options?: ODataOptions): Record<string, object>;
1038
+ namespace odata {
1039
+ /**
1040
+ * Renders the given CSN into EDM in JSON format _and_ XML format for each service.
1041
+ * That is, it is a combination of `to.edm.all()` and `to.edmx.all()`.
1042
+ * If `options.serviceNames` is not set, all services will be rendered.
1043
+ *
1044
+ * @returns A map of `'<protocol>': object` where each entry is `'<serviceName>': object` entries where
1045
+ * each entry consists of `{edmx: string, edm?: object}`.
1046
+ * @since v4.6.0
1047
+ */
1048
+ function all(csn: CSN, options: ODataOptions): Record<string, object>;
1049
+ }
1050
+
1026
1051
  /**
1027
1052
  * Renders the given CSN into EDM (JSON format). Requires `options.service` to be set.
1028
1053
  *
package/lib/main.js CHANGED
@@ -158,6 +158,9 @@ module.exports = {
158
158
  edmx: Object.assign((...args) => snapi.edmx(...args), {
159
159
  all: (...args) => snapi.edmx.all(...args)
160
160
  }),
161
+ odata: Object.assign((...args) => snapi.odata2(...args), {
162
+ all: (...args) => snapi.odata2.all(...args)
163
+ }),
161
164
  },
162
165
  // Convenience for hdbtabledata calculation in @sap/cds
163
166
  getArtifactCdsPersistenceName: (...args) => csnUtils.getArtifactDatabaseNameOf(...args),
@@ -613,7 +613,7 @@ function csnRefs( csn, universalReady ) {
613
613
  return resolvePath( path, alias._select || alias._ref, null,
614
614
  'alias', ncache.$queryNumber );
615
615
  }
616
- const mixin = ncache._select.mixin && ncache._select.mixin[head];
616
+ const mixin = ncache._select.mixin?.[head];
617
617
  if (mixin && {}.hasOwnProperty.call( ncache._select.mixin, head )) {
618
618
  setCache( mixin, '_parent', qcache._select );
619
619
  return resolvePath( path, mixin, null, 'mixin', ncache.$queryNumber );
@@ -818,6 +818,9 @@ function csnRefs( csn, universalReady ) {
818
818
  as = `$_column_${ colIndex + 1 }`;
819
819
  setCache( col, '$as', as );
820
820
  let type = parentElementOrQueryCache;
821
+ if (col.cast)
822
+ traverseType( col.cast, col, 'column', colIndex, initNode );
823
+
821
824
  while (type.items)
822
825
  type = type.items;
823
826
  const elem = setCache( col, '_element', type.elements[as] );
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const { csnRefs, implicitAs } = require('../model/csnRefs');
3
+ const { csnRefs, implicitAs, pathId } = require('./csnRefs');
4
4
  const { applyTransformations, applyTransformationsOnNonDictionary, applyTransformationsOnDictionary } = require('../transform/db/applyTransformations');
5
- const { isBuiltinType } = require('../compiler/builtins.js');
5
+ const { isBuiltinType, isMagicVariable, isAnnotationExpression } = require('../compiler/builtins.js');
6
6
  const {
7
7
  sortCsn,
8
8
  cloneCsnDictionary: _cloneCsnDictionary,
@@ -1088,18 +1088,21 @@ function getLastPartOfRef( ref ) {
1088
1088
  * @param {object} toNode
1089
1089
  * @param {boolean} [overwrite]
1090
1090
  * @param {object} excludes
1091
+ * @param {array} annoNames (copy only these annotations or all if undefined)
1092
+ * @returns {array} copiedAnnoNames
1091
1093
  */
1092
- function copyAnnotations( fromNode, toNode, overwrite = false, excludes = {} ) {
1094
+ function copyAnnotations( fromNode, toNode, overwrite = false, excludes = {}, annoNames = undefined ) {
1093
1095
  // Ignore if no toNode (in case of errors)
1094
1096
  if (!toNode)
1095
1097
  return;
1096
1098
 
1097
- const annotations = Object.keys(fromNode).filter(key => key.startsWith('@'));
1099
+ if (annoNames == null)
1100
+ annoNames = Object.keys(fromNode).filter(key => key.startsWith('@'));
1098
1101
 
1099
- for (const anno of annotations) {
1102
+ annoNames.forEach((anno) => {
1100
1103
  if ((toNode[anno] === undefined || overwrite) && !excludes[anno])
1101
1104
  toNode[anno] = cloneAnnotationValue(fromNode[anno]);
1102
- }
1105
+ });
1103
1106
  }
1104
1107
 
1105
1108
 
@@ -1446,7 +1449,7 @@ function isAssociationOperand( arg, path, inspectRef ) {
1446
1449
  }
1447
1450
 
1448
1451
  function pathName( ref ) {
1449
- return ref ? ref.map( id => id?.id || id ).join( '.' ) : '';
1452
+ return ref ? ref.map( pathId ).join( '.' ) : '';
1450
1453
  }
1451
1454
 
1452
1455
  module.exports = {
@@ -1456,6 +1459,8 @@ module.exports = {
1456
1459
  cloneCsnDictionary,
1457
1460
  cloneAnnotationValue,
1458
1461
  isBuiltinType,
1462
+ isMagicVariable,
1463
+ isAnnotationExpression,
1459
1464
  applyAnnotationsFromExtensions,
1460
1465
  forEachGeneric,
1461
1466
  forEachDefinition,
@@ -13,12 +13,12 @@ optionProcessor
13
13
  .option('-h, --help')
14
14
  .option('-v, --version')
15
15
  .option(' --options <file>')
16
- .option('-w, --warning <level>', ['0', '1', '2', '3'])
16
+ .option('-w, --warning <level>', { valid: ['0', '1', '2', '3'] })
17
17
  .option(' --quiet')
18
18
  .option(' --show-message-id')
19
19
  .option(' --no-message-id')
20
20
  .option(' --no-message-context')
21
- .option(' --color <mode>', ['auto', 'always', 'never'])
21
+ .option(' --color <mode>', { valid: ['auto', 'always', 'never'] })
22
22
  .option('-o, --out <dir>')
23
23
  .option(' --cds-home <dir>')
24
24
  .option(' --module-lookup-directories <list>')
@@ -34,13 +34,14 @@ optionProcessor
34
34
  .option(' --beta <list>')
35
35
  .option(' --deprecated <list>')
36
36
  .option(' --direct-backend')
37
- .option(' --fallback-parser <type>', ['cdl', 'csn', 'csn!'])
37
+ .option(' --fallback-parser <type>', { valid: ['cdl', 'csn', 'csn!'] })
38
38
  .option(' --shuffle <seed>') // 0 | 1..4294967296
39
39
  .option(' --test-mode')
40
40
  .option(' --test-sort-csn')
41
41
  .option(' --doc-comment')
42
42
  .option(' --add-texts-language-assoc')
43
43
  .option(' --localized-without-coalesce')
44
+ .option(' --tenant-as-column')
44
45
  .option(' --default-binary-length <length>')
45
46
  .option(' --default-string-length <length>')
46
47
  .option(' --no-recompile')
@@ -100,6 +101,7 @@ optionProcessor
100
101
  -E, --enrich-csn Show non-enumerable CSN properties and locations of references
101
102
  -R, --raw-output <name> Write XSN for definition "name" and error output to <stdout>,
102
103
  with name = "+", write complete XSN, long!
104
+ --tenant-as-column Add tenant fields to entities
103
105
  --internal-msg Write raw messages with call stack to <stdout>/<stderr>
104
106
  --beta-mode Enable all unsupported, incomplete (beta) features
105
107
  --beta <list> Comma separated list of unsupported, incomplete (beta) features to use.
@@ -164,7 +166,7 @@ optionProcessor
164
166
  // ----------- toHana -----------
165
167
  optionProcessor.command('H, toHana')
166
168
  .option('-h, --help')
167
- .option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: ['--names'] })
169
+ .option('-n, --sql-mapping <style>', { valid: ['plain', 'quoted', 'hdbcds'], aliases: ['--names'] })
168
170
  .option(' --render-virtual')
169
171
  .option(' --joinfk')
170
172
  .option('-u, --user <user>')
@@ -172,8 +174,8 @@ optionProcessor.command('H, toHana')
172
174
  .option('-c, --csn')
173
175
  .option(' --integrity-not-validated')
174
176
  .option(' --integrity-not-enforced')
175
- .option(' --assert-integrity <mode>', ['true', 'false', 'individual'])
176
- .option(' --assert-integrity-type <type>', ['RT', 'DB'], { ignoreCase: true })
177
+ .option(' --assert-integrity <mode>', { valid: ['true', 'false', 'individual'] })
178
+ .option(' --assert-integrity-type <type>', { valid: ['RT', 'DB'], ignoreCase: true })
177
179
  .option(' --pre2134ReferentialConstraintNames')
178
180
  .option(' --disable-hana-comments')
179
181
  .help(`
@@ -216,7 +218,7 @@ optionProcessor.command('H, toHana')
216
218
 
217
219
  optionProcessor.command('O, toOdata')
218
220
  .option('-h, --help')
219
- .option('-v, --odata-version <version>', ['v2', 'v4', 'v4x'], { aliases: ['--version'] })
221
+ .option('-v, --odata-version <version>', { valid: ['v2', 'v4', 'v4x'], aliases: ['--version'] })
220
222
  .option('-x, --xml')
221
223
  .option('-j, --json')
222
224
  .option(' --odata-containment')
@@ -228,8 +230,8 @@ optionProcessor.command('O, toOdata')
228
230
  .option(' --odata-v2-partial-constr')
229
231
  .option(' --odata-vocabularies <list>')
230
232
  .option('-c, --csn')
231
- .option('-f, --odata-format <format>', ['flat', 'structured'])
232
- .option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: [ '--names' ] })
233
+ .option('-f, --odata-format <format>', { valid: ['flat', 'structured'] })
234
+ .option('-n, --sql-mapping <style>', { valid: ['plain', 'quoted', 'hdbcds'], aliases: [ '--names' ] })
233
235
  .option('-s, --service-names <list>')
234
236
  .option(' --fewer-localized-views')
235
237
  .help(`
@@ -285,18 +287,18 @@ optionProcessor.command('C, toCdl')
285
287
 
286
288
  optionProcessor.command('Q, toSql')
287
289
  .option('-h, --help')
288
- .option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: [ '--names' ] })
289
- .option('-d, --sql-dialect <dialect>', ['hana', 'sqlite', 'plain', 'postgres', 'h2'], { aliases: [ '--dialect' ] })
290
+ .option('-n, --sql-mapping <style>', { valid: ['plain', 'quoted', 'hdbcds'], aliases: [ '--names' ] })
291
+ .option('-d, --sql-dialect <dialect>', { valid: ['hana', 'sqlite', 'plain', 'postgres', 'h2'], aliases: [ '--dialect' ] })
290
292
  .option(' --render-virtual')
291
293
  .option(' --joinfk')
292
294
  .option('-u, --user <user>')
293
295
  .option('-l, --locale <locale>')
294
- .option('-s, --src <style>', ['sql', 'hdi'])
296
+ .option('-s, --src <style>', { valid: ['sql', 'hdi'] })
295
297
  .option('-c, --csn')
296
298
  .option(' --integrity-not-validated')
297
299
  .option(' --integrity-not-enforced')
298
- .option(' --assert-integrity <mode>', ['true', 'false', 'individual'])
299
- .option(' --assert-integrity-type <type>', ['RT', 'DB'], { ignoreCase: true })
300
+ .option(' --assert-integrity <mode>', { valid: ['true', 'false', 'individual'] })
301
+ .option(' --assert-integrity-type <type>', { valid: ['RT', 'DB'], ignoreCase: true })
300
302
  .option(' --constraints-in-create-table')
301
303
  .option(' --pre2134ReferentialConstraintNames')
302
304
  .option(' --disable-hana-comments')
@@ -364,7 +366,7 @@ optionProcessor.command('Q, toSql')
364
366
 
365
367
  optionProcessor.command('toRename')
366
368
  .option('-h, --help')
367
- .option('-n, --sql-mapping <style>', ['quoted', 'hdbcds'], { aliases: ['--names'] })
369
+ .option('-n, --sql-mapping <style>', { valid: ['quoted', 'hdbcds'], aliases: ['--names'] })
368
370
  .help(`
369
371
  Usage: cdsc toRename [options] <files...>
370
372
 
@@ -386,14 +388,14 @@ optionProcessor.command('toRename')
386
388
 
387
389
  optionProcessor.command('manageConstraints')
388
390
  .option('-h, --help')
389
- .option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: ['--names'] })
390
- .option('-s, --src <style>', ['sql', 'hdi'])
391
+ .option('-n, --sql-mapping <style>', { valid: ['plain', 'quoted', 'hdbcds'], aliases: ['--names'] })
392
+ .option('-s, --src <style>', { valid: ['sql', 'hdi'] })
391
393
  .option(' --drop')
392
394
  .option(' --alter')
393
395
  .option(' --violations')
394
396
  .option(' --integrity-not-validated')
395
397
  .option(' --integrity-not-enforced')
396
- .option('-d, --sql-dialect <dialect>', ['hana', 'sqlite', 'plain', 'postgres', 'h2'], { aliases: [ '--dialect' ] })
398
+ .option('-d, --sql-dialect <dialect>', { valid: ['hana', 'sqlite', 'plain', 'postgres', 'h2'], aliases: [ '--dialect' ] })
397
399
  .help(`
398
400
  Usage: cdsc manageConstraints [options] <files...>
399
401
 
@@ -432,7 +434,7 @@ optionProcessor.command('manageConstraints')
432
434
 
433
435
  optionProcessor.command('toCsn')
434
436
  .option('-h, --help')
435
- .option('-f, --csn-flavor <flavor>', ['client', 'gensrc', 'universal'], { aliases: ['--flavor'] })
437
+ .option('-f, --csn-flavor <flavor>', { valid: ['client', 'gensrc', 'universal'], aliases: ['--flavor'] })
436
438
  .option(' --with-localized')
437
439
  .option(' --with-locations')
438
440
  .option(' --struct-xpr')
@@ -6,8 +6,6 @@ const {
6
6
  getResultingName,
7
7
  } = require('../model/csnUtils');
8
8
  const { forEach } = require('../utils/objectUtils');
9
- const { makeMessageFunction } = require('../base/messages');
10
- const { optionProcessor } = require('../optionProcessor');
11
9
  const { transformForRelationalDBWithCsn } = require('../transform/forRelationalDB');
12
10
 
13
11
  const {
@@ -20,14 +18,15 @@ const { sortCsn } = require('../json/to-csn');
20
18
  * Not part of our API, yet.
21
19
  *
22
20
  * @param {CSN.Model} csn
21
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
23
22
  * @param {CSN.Options} options
24
23
  */
25
- function alterConstraintsWithCsn( csn, options ) {
26
- const { error, warning } = makeMessageFunction(csn, options, 'manageConstraints');
24
+ function alterConstraintsWithCsn( csn, options, messageFunctions ) {
25
+ const { error, warning } = messageFunctions;
27
26
 
28
27
  const {
29
28
  drop, alter, src, violations, sqlDialect,
30
- } = options || {};
29
+ } = options;
31
30
 
32
31
  if (!sqlDialect || sqlDialect === 'h2' || sqlDialect === 'plain')
33
32
  warning(null, null, { prop: sqlDialect || 'plain' }, 'Referential Constraints are not available for sql dialect $(PROP)');
@@ -35,12 +34,11 @@ function alterConstraintsWithCsn( csn, options ) {
35
34
  if (drop && alter)
36
35
  error(null, null, 'Option “--drop” can\'t be combined with “--alter”');
37
36
 
38
-
39
- // Of course we want the database constraints
37
+ // Of course, we want the database constraints
40
38
  options.assertIntegrityType = options.assertIntegrityType || 'DB';
41
39
 
42
40
  const transformedOptions = _transformSqlOptions(csn, options);
43
- const forSqlCsn = transformForRelationalDBWithCsn(csn, transformedOptions, 'to.sql');
41
+ const forSqlCsn = transformForRelationalDBWithCsn(csn, transformedOptions, messageFunctions);
44
42
 
45
43
  if (violations && src && src !== 'sql') {
46
44
  error(null, null, { value: '--violations', othervalue: src },
@@ -56,32 +54,18 @@ function alterConstraintsWithCsn( csn, options ) {
56
54
  return intermediateResult;
57
55
  }
58
56
 
59
- function _transformSqlOptions( model, options ) {
57
+ // TODO: Remove / Move to api/options.js once alterConstraintsWithCsn is available outside bin/cdsc
58
+ function _transformSqlOptions( csn, options ) {
59
+ const { src } = options;
60
+ // eslint-disable-next-line global-require
61
+ const prepareOptions = require('../api/options');
62
+ options = prepareOptions.to.sql(options);
63
+ options.src = src;
60
64
  // Merge options with defaults.
61
65
  options = Object.assign({ sqlMapping: 'plain', sqlDialect: 'plain' }, options);
62
66
  options.toSql = true;
63
-
64
67
  if (!options.src && !options.csn)
65
68
  options.src = 'sql';
66
-
67
- const { warning, error } = makeMessageFunction(model, options, 'to.sql');
68
-
69
- optionProcessor.verifyOptions(options, 'toSql', true)
70
- // eslint-disable-next-line cds-compiler/message-template-string
71
- .forEach(complaint => warning(null, null, `${complaint}`));
72
-
73
- if (options.sqlDialect !== 'hana') {
74
- // CDXCORE-465, 'quoted' and 'hdbcds' are to be used in combination with dialect 'hana' only
75
- if (options.sqlMapping === 'quoted' || options.sqlMapping === 'hdbcds') {
76
- error(null, null, { value: options.sqlDialect, othervalue: options.sqlMapping },
77
- 'Option sqlDialect: $(VALUE) can\'t be combined with sqlMapping: $(OTHERVALUE)');
78
- }
79
-
80
- // No non-HANA SQL for HDI
81
- if (options.src === 'hdi')
82
- error(null, null, { value: options.sqlDialect }, 'Option sqlDialect: $(VALUE) can\'t be used for SAP HANA HDI');
83
- }
84
-
85
69
  return options;
86
70
  }
87
71
 
@@ -5,13 +5,11 @@ const { cdlNewLineRegEx } = require('../language/textUtils');
5
5
  const { findElement, createExpressionRenderer, withoutCast } = require('./utils/common');
6
6
  const { escapeString, hasUnpairedUnicodeSurrogate } = require('./utils/stringEscapes');
7
7
  const { checkCSNVersion } = require('../json/csnVersion');
8
- const { timetrace } = require('../utils/timetrace');
9
8
  const { forEachDefinition, normalizeTypeRef } = require('../model/csnUtils');
10
9
  const enrichUniversalCsn = require('../transform/universalCsn/universalCsnEnricher');
11
10
  const { isBetaEnabled } = require('../base/model');
12
11
  const { ModelError } = require('../base/error');
13
- const { makeMessageFunction } = require('../base/messages.js');
14
- const { typeParameters, specialFunctions, xprInAnnoProperties } = require('../compiler/builtins');
12
+ const { typeParameters, specialFunctions, isAnnotationExpression } = require('../compiler/builtins');
15
13
  const { forEach } = require('../utils/objectUtils');
16
14
  const {
17
15
  isBuiltinType,
@@ -30,14 +28,11 @@ const specialFunctionKeywords = Object.create(null);
30
28
  * - `namespace`: Namespace statement + `using from './model.cds'.
31
29
  *
32
30
  * @param {CSN.Model} csn
33
- * @param {CSN.Options} [options]
31
+ * @param {CSN.Options} options
32
+ * @param {object} msg Message Functions
34
33
  */
35
- function csnToCdl( csn, options ) {
34
+ function csnToCdl( csn, options, msg ) {
36
35
  const special$self = !csn?.definitions?.$self && '$self';
37
- timetrace.start('CDL rendering');
38
-
39
- const msg = makeMessageFunction(csn, options, 'to.cdl');
40
-
41
36
  if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn')) {
42
37
  // Since the expander modifies the CSN, we need to clone it first or
43
38
  // toCdl can't guarantee that the input CSN is not modified.
@@ -91,8 +86,6 @@ function csnToCdl( csn, options ) {
91
86
  cdlResult.model = usingsStr + cdlResult.model;
92
87
  }
93
88
 
94
- timetrace.stop('CDL rendering');
95
-
96
89
  msg.throwWithError();
97
90
  return cdlResult;
98
91
 
@@ -1353,7 +1346,7 @@ function csnToCdl( csn, options ) {
1353
1346
  result += renderNullability(artifact);
1354
1347
 
1355
1348
  if (artifact.default && !artifact.on)
1356
- result += ` default ${exprRenderer.renderExpr(artifact.default, env.withSubPath([ 'default' ]))}`;
1349
+ result += renderDefaultExpr(artifact, env);
1357
1350
  return result;
1358
1351
  }
1359
1352
 
@@ -1386,7 +1379,7 @@ function csnToCdl( csn, options ) {
1386
1379
  // If there is a default value, and it's a calculated element, do not
1387
1380
  // render the default (because it's not supported for calc elements).
1388
1381
  if (artifact.default !== undefined && !artifact.value)
1389
- result += ` default ${exprRenderer.renderExpr(artifact.default, env.withSubPath([ 'default' ]))}`;
1382
+ result += renderDefaultExpr(artifact, env);
1390
1383
 
1391
1384
  return result;
1392
1385
  }
@@ -1463,8 +1456,7 @@ function csnToCdl( csn, options ) {
1463
1456
  * @param {CdlRenderEnvironment} env
1464
1457
  */
1465
1458
  function renderAnnotationValue( annoValue, env ) {
1466
- const isXpr = annoValue?.['='] !== undefined && xprInAnnoProperties.some(xProp => annoValue[xProp] !== undefined);
1467
- if (isXpr) {
1459
+ if (isAnnotationExpression(annoValue)) {
1468
1460
  // Once inside an expression, we stay there.
1469
1461
  const xpr = exprRenderer.renderExpr(annoValue, env);
1470
1462
  return `( ${xpr} )`;
@@ -1758,6 +1750,17 @@ function csnToCdl( csn, options ) {
1758
1750
  return result;
1759
1751
  }
1760
1752
 
1753
+ function renderDefaultExpr( art, env ) {
1754
+ if (!art.default)
1755
+ return '';
1756
+ let result = ' default ';
1757
+ if ( art.default.xpr && xprContainsCondition( art.default.xpr))
1758
+ result += exprRenderer.renderSubExpr(withoutCast( art.default), env.withSubPath([ 'default' ]));
1759
+ else
1760
+ result += exprRenderer.renderExpr(withoutCast( art.default), env.withSubPath([ 'default' ]));
1761
+ return result;
1762
+ }
1763
+
1761
1764
  // Render the nullability of an element or parameter (can be unset, true, or false)
1762
1765
  function renderNullability( obj /* , env */) {
1763
1766
  if (obj.notNull === undefined) {