@sap/cds-compiler 2.4.4 → 2.10.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 (106) hide show
  1. package/CHANGELOG.md +241 -1
  2. package/bin/.eslintrc.json +17 -0
  3. package/bin/cds_update_identifiers.js +8 -7
  4. package/bin/cdsc.js +180 -132
  5. package/bin/cdshi.js +18 -11
  6. package/bin/cdsse.js +38 -32
  7. package/bin/cdsv2m.js +8 -7
  8. package/doc/CHANGELOG_BETA.md +36 -1
  9. package/lib/api/main.js +81 -100
  10. package/lib/api/options.js +17 -11
  11. package/lib/api/validate.js +12 -8
  12. package/lib/backends.js +0 -81
  13. package/lib/base/keywords.js +32 -2
  14. package/lib/base/location.js +2 -2
  15. package/lib/base/message-registry.js +66 -4
  16. package/lib/base/messages.js +84 -27
  17. package/lib/base/model.js +2 -61
  18. package/lib/checks/arrayOfs.js +0 -1
  19. package/lib/checks/defaultValues.js +27 -2
  20. package/lib/checks/elements.js +1 -6
  21. package/lib/checks/enricher.js +8 -2
  22. package/lib/checks/foreignKeys.js +0 -6
  23. package/lib/checks/managedWithoutKeys.js +17 -0
  24. package/lib/checks/nonexpandableStructured.js +38 -0
  25. package/lib/checks/onConditions.js +9 -45
  26. package/lib/checks/queryNoDbArtifacts.js +27 -9
  27. package/lib/checks/selectItems.js +25 -2
  28. package/lib/checks/types.js +26 -2
  29. package/lib/checks/unknownMagic.js +38 -0
  30. package/lib/checks/utils.js +61 -0
  31. package/lib/checks/validator.js +66 -13
  32. package/lib/compiler/assert-consistency.js +24 -12
  33. package/lib/compiler/builtins.js +2 -0
  34. package/lib/compiler/checks.js +6 -4
  35. package/lib/compiler/definer.js +101 -39
  36. package/lib/compiler/index.js +88 -59
  37. package/lib/compiler/resolver.js +455 -209
  38. package/lib/compiler/shared.js +57 -33
  39. package/lib/edm/annotations/genericTranslation.js +183 -187
  40. package/lib/edm/csn2edm.js +128 -99
  41. package/lib/edm/edm.js +18 -21
  42. package/lib/edm/edmPreprocessor.js +361 -127
  43. package/lib/edm/edmUtils.js +103 -33
  44. package/lib/gen/Dictionary.json +74 -28
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +18 -4
  47. package/lib/gen/language.tokens +124 -118
  48. package/lib/gen/languageLexer.interp +13 -1
  49. package/lib/gen/languageLexer.js +870 -839
  50. package/lib/gen/languageLexer.tokens +116 -111
  51. package/lib/gen/languageParser.js +5894 -5614
  52. package/lib/json/from-csn.js +152 -67
  53. package/lib/json/to-csn.js +334 -135
  54. package/lib/language/antlrParser.js +4 -3
  55. package/lib/language/errorStrategy.js +1 -0
  56. package/lib/language/genericAntlrParser.js +24 -14
  57. package/lib/language/language.g4 +188 -128
  58. package/lib/main.d.ts +435 -0
  59. package/lib/main.js +31 -7
  60. package/lib/model/api.js +78 -0
  61. package/lib/model/csnRefs.js +463 -187
  62. package/lib/model/csnUtils.js +280 -136
  63. package/lib/model/enrichCsn.js +75 -4
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/modelCompare/compare.js +70 -25
  66. package/lib/optionProcessor.js +13 -10
  67. package/lib/render/.eslintrc.json +4 -1
  68. package/lib/render/DuplicateChecker.js +8 -5
  69. package/lib/render/toCdl.js +123 -40
  70. package/lib/render/toHdbcds.js +156 -65
  71. package/lib/render/toSql.js +87 -11
  72. package/lib/render/utils/common.js +55 -9
  73. package/lib/render/utils/sql.js +3 -3
  74. package/lib/sql-identifier.js +6 -1
  75. package/lib/transform/{sql → db}/.eslintrc.json +0 -0
  76. package/lib/transform/{sql → db}/assertUnique.js +7 -8
  77. package/lib/transform/{sql → db}/constraints.js +35 -20
  78. package/lib/transform/db/draft.js +353 -0
  79. package/lib/transform/db/expansion.js +582 -0
  80. package/lib/transform/db/flattening.js +325 -0
  81. package/lib/transform/{sql → db}/groupByOrderBy.js +8 -16
  82. package/lib/transform/{sql → db}/helpers.js +0 -0
  83. package/lib/transform/{sql → db}/transformExists.js +256 -60
  84. package/lib/transform/forHanaNew.js +216 -765
  85. package/lib/transform/forOdataNew.js +60 -56
  86. package/lib/transform/localized.js +48 -26
  87. package/lib/transform/odata/attachPath.js +19 -4
  88. package/lib/transform/odata/expandStructKeysInAssociations.js +2 -2
  89. package/lib/transform/odata/generateForeignKeyElements.js +13 -12
  90. package/lib/transform/odata/referenceFlattener.js +60 -36
  91. package/lib/transform/odata/sortByAssociationDependency.js +4 -4
  92. package/lib/transform/odata/structuralPath.js +76 -0
  93. package/lib/transform/odata/structureFlattener.js +21 -22
  94. package/lib/transform/odata/toFinalBaseType.js +5 -5
  95. package/lib/transform/odata/typesExposure.js +27 -17
  96. package/lib/transform/odata/utils.js +2 -2
  97. package/lib/transform/transformUtilsNew.js +141 -77
  98. package/lib/transform/translateAssocsToJoins.js +17 -14
  99. package/lib/transform/universalCsnEnricher.js +67 -0
  100. package/lib/utils/file.js +0 -11
  101. package/lib/utils/moduleResolve.js +6 -8
  102. package/lib/utils/timetrace.js +6 -1
  103. package/package.json +2 -1
  104. package/lib/base/deepCopy.js +0 -66
  105. package/lib/json/walker.js +0 -26
  106. package/lib/utils/string.js +0 -17
package/bin/cdsse.js CHANGED
@@ -15,8 +15,8 @@
15
15
  'use strict';
16
16
 
17
17
  const commands = {
18
- complete, find, lint
19
- }
18
+ complete, find, lint,
19
+ };
20
20
 
21
21
  const fs = require('fs');
22
22
  const path = require('path');
@@ -25,12 +25,12 @@ const main = require('../lib/main');
25
25
  const { locationString } = require('../lib/base/messages');
26
26
  const { availableBetaFlags: beta } = require('../lib/base/model');
27
27
 
28
- let argv = process.argv;
29
- let cmd = commands[ argv[2] ];
30
- var line = Number.parseInt( argv[3] );
31
- let column = Number.parseInt( argv[4] );
32
- let file = argv[5];
33
- let frel = path.relative( '', file||'' );
28
+ const { argv } = process;
29
+ const cmd = commands[argv[2]];
30
+ const line = Number.parseInt( argv[3] );
31
+ const column = Number.parseInt( argv[4] );
32
+ const file = argv[5];
33
+ const frel = path.relative( '', file || '' );
34
34
  // TODO: proper realname
35
35
 
36
36
  if (argv.length > 5 && cmd && line > 0 && column > 0)
@@ -62,17 +62,19 @@ function complete( err, buf ) {
62
62
  hasId = tokensAt( buf, off.prefix, off.col, false );
63
63
  }
64
64
  else {
65
- let charBefore = buf[ off.prefix-1 ];
66
- if ([':', '<', '.', '>', '!', '|', '='].includes( charBefore ))
65
+ const charBefore = buf[off.prefix - 1];
66
+ if ([ ':', '<', '.', '>', '!', '|', '=' ].includes( charBefore ))
67
67
  // If first of multi-char symbols from 'literalNames' in
68
68
  // gen/languageParser, calculate "symbol continuation"
69
- tokensAt( buf, off.prefix-1, off.col-1, charBefore );
69
+ tokensAt( buf, off.prefix - 1, off.col - 1, charBefore );
70
70
  hasId = tokensAt( buf, off.prefix, off.col, true );
71
71
  }
72
72
  if (hasId) {
73
- let src = buf.substring( 0, off.prefix ) + '__NO_SUCH_ID__' + buf.substring( off.cursor );
74
- let fname = path.resolve( '', file );
75
- compiler.compileX( [file], '', { attachValidNames: true, lintMode: true, beta, messages: [] } , { [fname]: src } )
73
+ const src = `${buf.substring( 0, off.prefix )}__NO_SUCH_ID__${buf.substring( off.cursor )}`;
74
+ const fname = path.resolve( '', file );
75
+ compiler.compileX( [ file ], '', {
76
+ attachValidNames: true, lintMode: true, beta, messages: [],
77
+ }, { [fname]: src } )
76
78
  .then( ident, ident );
77
79
  }
78
80
  return true;
@@ -80,11 +82,11 @@ function complete( err, buf ) {
80
82
  function ident( xsnOrErr ) {
81
83
  if (!(xsnOrErr.messages || xsnOrErr.options && xsnOrErr.options.messages))
82
84
  return usage( xsnOrErr );
83
- let vn = messageAt( xsnOrErr, 'validNames', off.col ) || Object.create(null);
85
+ const vn = messageAt( xsnOrErr, 'validNames', off.col ) || Object.create(null);
84
86
  // TODO: if there is no such message, use console.log( 'arbitrary identifier' )
85
87
  // if we want to avoid that the editor switches to fuzzy completion match
86
88
  // against the prefix (not yet done anyway)
87
- for (let n in vn)
89
+ for (const n in vn)
88
90
  console.log( n, vn[n].kind );
89
91
  if (!Object.keys( vn ).length)
90
92
  console.log( 'unknown_identifier', 'identifier' );
@@ -104,19 +106,21 @@ function find( err, buf ) {
104
106
  return usage();
105
107
  if (off.prefix === off.cursor) // not at name
106
108
  return true;
107
- const src = buf.substring( 0, off.prefix ) + '__NO_SUCH_ID__' + buf.substring( off.cursor );
109
+ const src = `${buf.substring( 0, off.prefix )}__NO_SUCH_ID__${buf.substring( off.cursor )}`;
108
110
  const fname = path.resolve( '', file );
109
- compiler.compileX( [file], '', { attachValidNames: true, lintMode: true, beta, messages: [] } , { [fname]: src } )
111
+ compiler.compileX( [ file ], '', {
112
+ attachValidNames: true, lintMode: true, beta, messages: [],
113
+ }, { [fname]: src } )
110
114
  .then( show, show );
111
115
  return true;
112
116
 
113
- function show( xsnOrErr ) {
117
+ function show( xsnOrErr ) {
114
118
  if (!(xsnOrErr.messages || xsnOrErr.options && xsnOrErr.options.messages))
115
119
  return usage( xsnOrErr );
116
- let vn = messageAt( xsnOrErr, 'validNames', off.col ) || Object.create(null);
120
+ const vn = messageAt( xsnOrErr, 'validNames', off.col ) || Object.create(null);
117
121
  const art = vn[buf.substring( off.prefix, off.cursor )];
118
122
  if (art)
119
- console.log( locationString( art.name.location || art.location ) + ': Definition' );
123
+ console.log( `${locationString( art.name.location || art.location )}: Definition` );
120
124
  return true;
121
125
  }
122
126
  }
@@ -124,25 +128,25 @@ function find( err, buf ) {
124
128
  function lint( err, buf ) {
125
129
  if (err)
126
130
  return usage( err );
127
- let fname = path.resolve( '', file );
128
- compiler.compileX( [file], '', { lintMode: true, beta, messages: [] }, { [fname]: buf } )
131
+ const fname = path.resolve( '', file );
132
+ compiler.compileX( [ file ], '', { lintMode: true, beta, messages: [] }, { [fname]: buf } )
129
133
  .then( display, display );
130
134
  return true;
131
135
 
132
136
  function display( xsnOrErr ) {
133
- let messages = xsnOrErr.messages || xsnOrErr.options && xsnOrErr.options.messages;
137
+ const messages = xsnOrErr.messages || xsnOrErr.options && xsnOrErr.options.messages;
134
138
  if (!messages)
135
139
  return usage( xsnOrErr );
136
- for (let msg of messages)
140
+ for (const msg of messages)
137
141
  console.log( main.messageString( msg ) );
138
142
  return true;
139
143
  }
140
144
  }
141
145
 
142
146
  function tokensAt( buf, offset, col, symbol ) {
143
- let src = buf.substring( 0, offset ) + '≠' + buf.substring( offset );
144
- let et = messageAt( compiler.parseX( src, frel, { messages: [] } ), 'expectedTokens', col ) || [];
145
- for (let n of et) {
147
+ const src = `${buf.substring( 0, offset )}≠${buf.substring( offset )}`;
148
+ const et = messageAt( compiler.parseX( src, frel, { messages: [] } ), 'expectedTokens', col ) || [];
149
+ for (const n of et) {
146
150
  if (typeof symbol === 'string') {
147
151
  if (n.length > 3 && n.charAt(0) === "'" && n.charAt(1) === symbol)
148
152
  console.log( n.slice( 2, -1 ), 'symbolCont' );
@@ -154,15 +158,17 @@ function tokensAt( buf, offset, col, symbol ) {
154
158
  else if (/^[A-Z_]+$/.test( n )) {
155
159
  console.log( n.toLowerCase(), 'keyword' );
156
160
  }
157
- else if (n !== 'Identifier')
161
+ else if (n !== 'Identifier') {
158
162
  console.log( n, 'unknown' );
163
+ }
159
164
  }
160
165
  return et.includes( 'Identifier' );
161
166
  }
162
167
 
163
168
  function messageAt( model, prop, col ) {
164
- let msg = (model.messages || model.options && model.options.messages).find(
165
- m => m[prop] && m.location.line === line && m.location.col === col && m.location.file === frel );
169
+ const msg = (model.messages || model.options && model.options.messages).find(
170
+ m => m[prop] && m.location.line === line && m.location.col === col && m.location.file === frel
171
+ );
166
172
  return msg && msg[prop];
167
173
  }
168
174
 
@@ -173,7 +179,7 @@ function messageAt( model, prop, col ) {
173
179
  */
174
180
  function offset( buf, alsoSuffix ) { // for line and column
175
181
  let pos = 0;
176
- for (let l = line-1; l; --l) {
182
+ for (let l = line - 1; l; --l) {
177
183
  pos = buf.indexOf( '\n', pos ) + 1;
178
184
  if (!pos)
179
185
  return false;
package/bin/cdsv2m.js CHANGED
@@ -1,17 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ 'use strict';
4
+
3
5
  // Very simple command-line interface to support model migration from compiler
4
6
  // v1 to v2. (If our client command processor would have been not as difficult
5
7
  // as after #2629, we could have a cdsc commend "migrate...)
6
8
 
7
-
8
9
  const commands = {
9
- ria
10
- }
10
+ ria,
11
+ };
11
12
  const compiler = require('../lib/compiler');
12
13
 
13
- const argv = process.argv;
14
- const cmd = commands[ argv[2] ];
14
+ const { argv } = process;
15
+ const cmd = commands[argv[2]];
15
16
  const files = argv.slice(3);
16
17
  const options = { messages: [] };
17
18
 
@@ -36,8 +37,8 @@ function ria() {
36
37
  // regex match on message text not for productive code!
37
38
  for (const msgObj of msgs) {
38
39
  const matches = msgObj.message.match( /["“][^"”]+["”]/g );
39
- matches.slice(2).forEach( name => {
40
- annotates[ name.slice( 1, -1 ) ] = true;
40
+ matches.slice(2).forEach( (name) => {
41
+ annotates[name.slice( 1, -1 )] = true;
41
42
  } );
42
43
  }
43
44
  for (const name in annotates)
@@ -1,12 +1,48 @@
1
1
  # ChangeLog of Beta Features for cdx compiler and backends
2
2
 
3
3
  <!-- markdownlint-disable MD024 -->
4
+ <!-- markdownlint-disable MD004 -->
4
5
  <!-- (no-duplicate-heading)-->
5
6
 
6
7
  Note: `beta` fixes, changes and features are listed in this ChangeLog just for information.
7
8
  The compiler behavior concerning `beta` features can change at any time without notice.
8
9
  **Don't use `beta` fixes, changes and features in productive mode.**
9
10
 
11
+ ## Version 2.6.0
12
+
13
+ ### Removed `pretransformedCSN`
14
+
15
+ ### Removed `renderSql`
16
+
17
+ ### Removed `keylessManagedAssoc`
18
+
19
+ This is now the default - see CHANGELOG entry for 2.6.0
20
+
21
+ ### Fixed `nestedProjections`
22
+
23
+ - to.sql/hdi/hdbcds: now work correctly when nested projections are used
24
+
25
+ ### Fixed `foreignKeyConstraints`
26
+
27
+ - Always use the name of the association / backlink compared to
28
+ `$self` as name suffix for a constraint
29
+ - Composition of one always result in:
30
+ + ON DELETE RESTRICT
31
+ + ON UPDATE RESTRICT
32
+ - Composition of one w/o backlink will result in a constraint in
33
+ the entity where the composition is defined
34
+
35
+ ## Version 2.4.4
36
+
37
+ ### Added `nestedProjections`
38
+
39
+ - Support `expand`: columns can look like `assoc_or_struct_or_tabalias { col_expression1, … }`,
40
+ `longer.ref as name { *, … } excluding { … }`, `{ col_expression1 as sub1, … } as name`, etc.
41
+ - Support `inline`: columns can look like `assoc_or_struct_or_tabalias.{ col_expression1, … }`,
42
+ `longer.ref[filter = condition].{ *, … } excluding { … }`, `assoc_or_struct_or_tabalias.*`, etc.
43
+ - _Some checks are missing and will be added! Minor changes might occur._
44
+ - **The SQL backends might not work properly yet if nested projections are used!**
45
+
10
46
  ## Version 2.4.2
11
47
 
12
48
  ### Added `keylessManagedAssoc`
@@ -19,7 +55,6 @@ The compiler behavior concerning `beta` features can change at any time without
19
55
  Consequently, these associations are not added to the `WITH ASSOCIATIONS` clause or forwarded to HANA CDS.
20
56
  Managed Associations without foreign keys must be enabled with `--beta: keylessManagedAssoc`
21
57
 
22
-
23
58
  ## Version 2.4.0
24
59
 
25
60
  ### Changed `foreignKeyConstraints`
package/lib/api/main.js CHANGED
@@ -4,15 +4,18 @@
4
4
 
5
5
  const prepareOptions = require('./options');
6
6
  const backends = require('../backends');
7
- const { setProp, isBetaEnabled } = require('../base/model');
7
+ const { setProp } = require('../base/model');
8
8
  const { emptyLocation } = require('../base/location');
9
- const { CompilationError, makeMessageFunction, deduplicateMessages } = require('../base/messages');
10
- const { compactModel } = require('../json/to-csn');
9
+ const { CompilationError, makeMessageFunction } = require('../base/messages');
10
+ const { recompileX } = require('../compiler/index');
11
+ const { compactModel, sortCsn } = require('../json/to-csn');
11
12
  const { transform4odataWithCsn } = require('../transform/forOdataNew.js');
12
13
  const { toSqlDdl } = require('../render/toSql');
13
14
  const { compareModels } = require('../modelCompare/compare');
14
15
  const sortViews = require('../model/sortViews');
15
16
  const { getResultingName } = require('../model/csnUtils');
17
+ const timetrace = require('../utils/timetrace');
18
+ const { transformForHanaWithCsn } = require('../transform/forHanaNew');
16
19
 
17
20
  /**
18
21
  * Return the artifact name for use for the hdbresult object
@@ -31,10 +34,10 @@ const propertyToCheck = {
31
34
  };
32
35
 
33
36
  const { cloneCsn } = require('../model/csnUtils');
37
+ const { toHdbcdsSource } = require('../render/toHdbcds');
34
38
 
35
39
  const relevantGeneralOptions = [ /* for future generic options */ ];
36
40
  const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
37
- const relevantSqlOptions = [ 'sqlMapping', 'sqlDialect' ];
38
41
  const warnAboutMismatchOdata = [ 'odataVersion' ];
39
42
 
40
43
  /**
@@ -152,6 +155,49 @@ function cdl(csn, externalOptions = {}) {
152
155
  const { result } = backends.toCdlWithCsn(cloneCsn(csn, internalOptions), internalOptions);
153
156
  return result;
154
157
  }
158
+ /**
159
+ * Transform a CSN like to.sql
160
+ *
161
+ * @param {CSN.Model} csn Plain input CSN
162
+ * @param {sqlOptions} [options={}] Options
163
+ * @returns {CSN.Model} CSN transformed like to.sql
164
+ * @private
165
+ */
166
+ function forSql(csn, options = {}) {
167
+ const internalOptions = prepareOptions.to.sql(options);
168
+ internalOptions.toSql.csn = true;
169
+ return backends.toSqlWithCsn(csn, internalOptions).csn;
170
+ }
171
+ /**
172
+ * Transform a CSN like to.hdi
173
+ *
174
+ * @param {CSN.Model} csn Plain input CSN
175
+ * @param {hdiOptions} [options={}] Options
176
+ * @returns {CSN.Model} CSN transformed like to.hdi
177
+ * @private
178
+ */
179
+ function forHdi(csn, options = {}) {
180
+ const internalOptions = prepareOptions.to.hdi(options);
181
+ internalOptions.toSql.csn = true;
182
+ return backends.toSqlWithCsn(csn, internalOptions).csn;
183
+ }
184
+ /**
185
+ * Transform a CSN like to.hdbcds
186
+ *
187
+ * @param {CSN.Model} csn Plain input CSN
188
+ * @param {hdbcdsOptions} [options={}] Options
189
+ * @returns {CSN.Model} CSN transformed like to.hdbcds
190
+ * @private
191
+ */
192
+ function forHdbcds(csn, options = {}) {
193
+ const internalOptions = prepareOptions.to.hdbcds(options);
194
+ internalOptions.transformation = 'hdbcds';
195
+
196
+ const hanaCsn = transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
197
+
198
+ return internalOptions.testMode ? sortCsn(hanaCsn, internalOptions) : hanaCsn;
199
+ }
200
+
155
201
  /**
156
202
  * Process the given CSN into SQL.
157
203
  *
@@ -165,18 +211,7 @@ function sql(csn, options = {}) {
165
211
  // we need the CSN for view sorting
166
212
  internalOptions.toSql.csn = true;
167
213
 
168
- let intermediateResult;
169
- if (isBetaEnabled(internalOptions, 'pretransformedCSN') && isPreTransformed(csn, 'sql')) {
170
- internalOptions.noRecompile = true; // pre-transformed cannot be recompiled!
171
- checkPreTransformedCsn(csn, internalOptions, relevantSqlOptions, warnAboutMismatchOdata, 'to.sql');
172
-
173
- intermediateResult = backends.renderSqlWithCsn(csn, internalOptions);
174
- // attach CSN for view sorting
175
- intermediateResult.csn = cloneCsn(csn, internalOptions);
176
- }
177
- else {
178
- intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
179
- }
214
+ const intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
180
215
 
181
216
  const result = sortViews(intermediateResult);
182
217
 
@@ -338,7 +373,7 @@ function hdiMigration(csn, options, beforeImage) {
338
373
  const afterImage = backends.toSqlWithCsn(csn, internalOptions).csn;
339
374
 
340
375
  // Compare both images.
341
- const diff = compareModels(beforeImage || afterImage, afterImage);
376
+ const diff = compareModels(beforeImage || afterImage, afterImage, internalOptions);
342
377
 
343
378
  // Convert the diff to SQL.
344
379
  internalOptions.forHana = true; // Make it pass the SQL rendering
@@ -405,10 +440,15 @@ hdi.migration = hdiMigration;
405
440
  * @returns {HDBCDS} { <filename>:<content>, ...}
406
441
  */
407
442
  function hdbcds(csn, options = {}) {
443
+ timetrace.start('to.hdbcds');
408
444
  const internalOptions = prepareOptions.to.hdbcds(options);
409
- const intermediateResult = backends.toHanaWithCsn(csn, internalOptions);
445
+ internalOptions.transformation = 'hdbcds';
410
446
 
411
- return flattenResultStructure(intermediateResult);
447
+ const hanaCsn = forHdbcds(csn, internalOptions);
448
+
449
+ const result = flattenResultStructure(toHdbcdsSource(hanaCsn, internalOptions));
450
+ timetrace.stop();
451
+ return result;
412
452
  }
413
453
  /**
414
454
  * Generate a edm document for the given service
@@ -454,19 +494,17 @@ function edmall(csn, options = {}) {
454
494
  if (internalOptions.version === 'v2')
455
495
  error(null, null, 'OData JSON output is not available for OData V2');
456
496
 
457
- let servicesAll;
458
-
459
497
  const result = {};
498
+ let oDataCsn = csn;
460
499
 
461
- if (isPreTransformed(csn, 'odata')) {
500
+ if (isPreTransformed(csn, 'odata'))
462
501
  checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
463
- servicesAll = backends.preparedCsnToEdmAll(csn, internalOptions);
464
- }
465
- else {
466
- const oDataCsn = odataInternal(csn, internalOptions);
467
- servicesAll = backends.preparedCsnToEdmAll(oDataCsn, internalOptions);
468
- }
469
- const services = servicesAll.edmj;
502
+
503
+ else
504
+ oDataCsn = odataInternal(csn, internalOptions);
505
+
506
+ const servicesJson = backends.preparedCsnToEdmAll(oDataCsn, internalOptions);
507
+ const services = servicesJson.edmj;
470
508
  for (const serviceName in services) {
471
509
  const lEdm = services[serviceName];
472
510
  // FIXME: Why only metadata_json - isn't this rather a 'combined_json' ? If so, rename it!
@@ -555,41 +593,6 @@ function flattenResultStructure(toProcess) {
555
593
  return result;
556
594
  }
557
595
 
558
- /**
559
- * Compute the .hdbcds files that would have been generated by the csn for an undeploy.json.
560
- * This is needed for the handover between hdbcds and hdbtable - the existing hdbcds artifacts need to be undeployed.
561
- *
562
- * @param {any} csn A clean input CSN
563
- * @param {hdbcdsOptions} [options={}] Options
564
- * @returns {string[]} Array of .hdbcds filenames
565
- */
566
- function undeploy(csn, options) {
567
- const hdbcdsResult = hdbcds(csn, options);
568
-
569
- return Object.keys(hdbcdsResult);
570
- }
571
-
572
- /**
573
- * This function simply renders the given CSN to SQL - no integrity checks, no transformations, no guarantees for correctness.
574
- * Strictly for internal evaluation!
575
- *
576
- * @param {CSN.Model} csn A CSN - for things to work correctly, this is expected to be a DB transformed CSN. Plain CSN might work - or might not.
577
- * @param {sqlOptions} [options={}] Options
578
- * @returns {SQL[]} Array of SQL statements, tables first, views second - the resulting statements might not be a consistent, deployable state!
579
- */
580
- function renderSQL(csn, options) {
581
- const internalOptions = prepareOptions.to.sql(options);
582
- if (!isBetaEnabled(internalOptions, 'renderSQL'))
583
- throw new Error('renderSQL is only available with beta-flag "renderSQL".');
584
-
585
-
586
- // Add flag so it thinks we ran through forHanaNew
587
- internalOptions.forHana = true;
588
-
589
- const sqlResult = toSqlDdl(csn, internalOptions);
590
- return Object.values(flattenResultStructure(sqlResult));
591
- }
592
-
593
596
  module.exports = {
594
597
  odata: publishCsnProcessor(odata, 'for.odata'),
595
598
  cdl: publishCsnProcessor(cdl, 'to.cdl'),
@@ -598,44 +601,14 @@ module.exports = {
598
601
  hdbcds: publishCsnProcessor(hdbcds, 'to.hdbcds'),
599
602
  edm: publishCsnProcessor(edm, 'to.edm'),
600
603
  edmx: publishCsnProcessor(edmx, 'to.edmx'),
601
- renderSQL,
602
- undeploy,
604
+ /** Internal only */
605
+ for_sql: publishCsnProcessor(forSql, 'for.sql'),
606
+ for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
607
+ for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
608
+ /** */
603
609
  };
604
610
 
605
611
 
606
- /**
607
- * Recompile the given CSN
608
- *
609
- * @param {object} csn Input CSN to recompile to XSN
610
- * @param {object} options Options
611
- * @returns {object} XSN
612
- *
613
- * TODO: move to lib/compiler/, consider new $recompile option, probaby issue
614
- * message api-recompiled-csn there.
615
- */
616
- function recompile(csn, options) {
617
- // TODO: is it really a good idea to set options in the input parameter?
618
- // (as long as we have the messages meddling of the option processor...)
619
- // OK, I try a copy
620
- options = { ...options };
621
- // Explicitly set parseCdl to false because backends cannot handle
622
- // the option and is only intended for CDL sources.
623
- options.parseCdl = false; // TODO: delete this option
624
- // Explicitly delete all toCsn options
625
- delete options.toCsn;
626
- /* eslint-disable global-require */
627
- const { augment } = require('../json/from-csn');
628
- const { compileSourcesX } = require('../compiler');
629
- /* eslint-enable global-require */
630
- const file = csn.$location && csn.$location.file &&
631
- csn.$location.file.replace(/[.]cds$/, '.cds.csn') || '<recompile>.csn';
632
- const xsn = augment(csn, file, options); // in-place
633
- const compiled = compileSourcesX( { [file]: xsn }, { ...options, $recompile: true } );
634
- if (options.messages)
635
- deduplicateMessages(options.messages);
636
- return compiled;
637
- }
638
-
639
612
  /**
640
613
  * @param {any} processor CSN processor
641
614
  * @param {string} _name Name of the processor
@@ -675,7 +648,7 @@ function publishCsnProcessor( processor, _name ) {
675
648
  const { info } = makeMessageFunction( csn, options, 'compile' );
676
649
  info( 'api-recompiled-csn', emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
677
650
  // next line to be replaced by CSN parser call which reads the CSN object
678
- const xsn = recompile(csn, options);
651
+ const xsn = recompileX(csn, options);
679
652
  const recompiledCsn = compactModel(xsn);
680
653
  return processor( recompiledCsn, options, ...args );
681
654
  }
@@ -707,6 +680,12 @@ function publishCsnProcessor( processor, _name ) {
707
680
  * @typedef {'plain' | 'quoted' | 'hdbcds' } NamingMode
708
681
  */
709
682
 
683
+ /**
684
+ * Available SQL change modes
685
+ *
686
+ * @typedef {'alter' | 'drop' } SqlChangeMode
687
+ */
688
+
710
689
  /**
711
690
  * Available oData versions
712
691
  *
@@ -749,6 +728,8 @@ function publishCsnProcessor( processor, _name ) {
749
728
  *
750
729
  * @typedef {object} hdiOptions
751
730
  * @property {NamingMode} [sqlMapping='plain'] Naming mode to use
731
+ * @property {SqlChangeMode} [sqlChangeMode='alter'] SQL change mode to use (for changed columns)
732
+ * @property {boolean} [allowCsnDowngrade=false] Allow downgrades of CSN major version (for modelCompare)
752
733
  * @property {object} [beta] Enable experimental features - not for productive use!
753
734
  * @property {boolean} [longAutoexposed=false] Deprecated: Produce long names (with underscores) for autoexposed entities
754
735
  * @property {Map<string, number>} [severities={}] Map of message-id and severity that allows setting the severity for the given message
@@ -10,15 +10,18 @@ const publicOptionsNewAPI = [
10
10
  // GENERAL
11
11
  'beta',
12
12
  'deprecated',
13
+ 'addTextsLanguageAssoc',
13
14
  'localizedLanguageFallback', // why can't I define the option type here?
14
15
  'severities',
15
16
  'messages',
16
17
  'withLocations',
17
18
  'defaultStringLength',
19
+ 'csnFlavor',
18
20
  // DB
19
21
  'sqlDialect',
20
22
  'sqlMapping',
21
23
  'sqlChangeMode',
24
+ 'allowCsnDowngrade',
22
25
  'joinfk',
23
26
  'magicVars',
24
27
  // ODATA
@@ -49,6 +52,7 @@ const privateOptions = [
49
52
  'skipDbConstraints',
50
53
  'noRecompile',
51
54
  'internalMsg',
55
+ 'disableHanaComments', // in case of issues with hana comment rendering
52
56
  'dependentAutoexposed', // deprecated, no effect - TODO: safe to remove?
53
57
  'longAutoexposed', // deprecated, no effect - TODO: safe to remove?
54
58
  'localizedWithoutCoalesce', // deprecated version of 'localizedLanguageFallback',
@@ -66,10 +70,11 @@ const overallOptions = publicOptionsNewAPI.concat(privateOptions);
66
70
  * @param {FlatOptions} [hardRequire={}] Hard requirements to enforce
67
71
  * @param {object} [customValidators] Custom validations to run instead of defaults
68
72
  * @param {string[]} [combinationValidators] Option combinations to validate
73
+ * @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
69
74
  * @returns {TranslatedOptions} General cds options
70
75
  */
71
76
  function translateOptions(input = {}, defaults = {}, hardRequire = {},
72
- customValidators = {}, combinationValidators = []) {
77
+ customValidators = {}, combinationValidators = [], moduleName = '') {
73
78
  const options = Object.assign({}, defaults);
74
79
  const inputOptionNames = Object.keys(input);
75
80
  for (const name of overallOptions) {
@@ -88,6 +93,7 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
88
93
  // Validate the filtered input options
89
94
  // only "new-style" options are here
90
95
  validate(options,
96
+ moduleName,
91
97
  // TODO: is there a better place to specify the type of option values?
92
98
  Object.assign( {
93
99
  localizedLanguageFallback: generateStringValidator([ 'none', 'coalesce' ]),
@@ -139,11 +145,11 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
139
145
 
140
146
  module.exports = {
141
147
  to: {
142
- cdl: options => translateOptions(options),
148
+ cdl: options => translateOptions(options, undefined, undefined, undefined, undefined, 'to.cdl'),
143
149
  sql: (options) => {
144
150
  const hardOptions = { src: 'sql' };
145
151
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'plain' };
146
- const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ]);
152
+ const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ], 'to.sql');
147
153
 
148
154
  const result = Object.assign({}, processed);
149
155
  result.toSql = Object.assign({}, processed);
@@ -153,7 +159,7 @@ module.exports = {
153
159
  hdi: (options) => {
154
160
  const hardOptions = { src: 'hdi' };
155
161
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
156
- const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) });
162
+ const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdi');
157
163
 
158
164
  const result = Object.assign({}, processed);
159
165
  result.toSql = Object.assign({}, processed);
@@ -162,17 +168,17 @@ module.exports = {
162
168
  },
163
169
  hdbcds: (options) => {
164
170
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
165
- const processed = translateOptions(options, defaultOptions, {}, { sqlDialect: generateStringValidator([ 'hana' ]) });
171
+ const processed = translateOptions(options, defaultOptions, {}, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdbcds');
166
172
 
167
173
  const result = Object.assign({}, processed);
168
- result.toHana = Object.assign({}, processed);
174
+ result.forHana = Object.assign({}, processed);
169
175
 
170
176
  return result;
171
177
  },
172
178
  edm: (options) => {
173
179
  const hardOptions = { json: true, combined: true };
174
180
  const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
175
- const processed = translateOptions(options, defaultOptions, hardOptions, { odataVersion: generateStringValidator([ 'v4' ]) }, [ 'valid-structured' ]);
181
+ const processed = translateOptions(options, defaultOptions, hardOptions, { odataVersion: generateStringValidator([ 'v4' ]) }, [ 'valid-structured' ], 'to.edm');
176
182
 
177
183
  const result = Object.assign({}, processed);
178
184
  result.toOdata = Object.assign({}, processed);
@@ -184,7 +190,7 @@ module.exports = {
184
190
  const defaultOptions = {
185
191
  odataVersion: 'v4', odataFormat: 'flat',
186
192
  };
187
- const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ]);
193
+ const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ], 'to.edmx');
188
194
 
189
195
  const result = Object.assign({}, processed);
190
196
  result.toOdata = Object.assign({}, processed);
@@ -196,7 +202,7 @@ module.exports = {
196
202
 
197
203
  odata: (options) => {
198
204
  const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
199
- const processed = translateOptions(options, defaultOptions, undefined, undefined, [ 'valid-structured' ]);
205
+ const processed = translateOptions(options, defaultOptions, undefined, undefined, [ 'valid-structured' ], 'for.odata');
200
206
 
201
207
  const result = Object.assign({}, processed);
202
208
  result.toOdata = Object.assign({}, processed);
@@ -206,10 +212,10 @@ module.exports = {
206
212
  },
207
213
  hana: (options) => {
208
214
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
209
- const processed = translateOptions(options, defaultOptions);
215
+ const processed = translateOptions(options, defaultOptions, undefined, undefined, undefined, 'for.hana');
210
216
 
211
217
  const result = Object.assign({}, processed);
212
- result.toHana = Object.assign({}, processed);
218
+ result.forHana = Object.assign({}, processed);
213
219
 
214
220
 
215
221
  return result;