@sap/cds-compiler 2.15.8 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/CHANGELOG.md +102 -1590
  2. package/bin/.eslintrc.json +2 -1
  3. package/bin/cdsc.js +61 -46
  4. package/doc/API.md +11 -0
  5. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  6. package/doc/CHANGELOG_BETA.md +26 -5
  7. package/doc/CHANGELOG_DEPRECATED.md +55 -1
  8. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  9. package/doc/Versioning.md +20 -1
  10. package/lib/api/.eslintrc.json +2 -2
  11. package/lib/api/main.js +282 -156
  12. package/lib/api/options.js +17 -88
  13. package/lib/api/validate.js +6 -10
  14. package/lib/base/keywords.js +280 -110
  15. package/lib/base/message-registry.js +85 -25
  16. package/lib/base/messages.js +119 -89
  17. package/lib/base/model.js +46 -2
  18. package/lib/base/optionProcessorHelper.js +53 -21
  19. package/lib/checks/actionsFunctions.js +15 -12
  20. package/lib/checks/annotationsOData.js +1 -1
  21. package/lib/checks/cdsPersistence.js +1 -0
  22. package/lib/checks/elements.js +6 -6
  23. package/lib/checks/invalidTarget.js +1 -1
  24. package/lib/checks/nonexpandableStructured.js +1 -1
  25. package/lib/checks/queryNoDbArtifacts.js +2 -1
  26. package/lib/checks/selectItems.js +101 -15
  27. package/lib/checks/types.js +7 -8
  28. package/lib/checks/utils.js +2 -2
  29. package/lib/checks/validator.js +3 -3
  30. package/lib/compiler/assert-consistency.js +78 -21
  31. package/lib/compiler/base.js +6 -4
  32. package/lib/compiler/builtins.js +177 -10
  33. package/lib/compiler/checks.js +1 -1
  34. package/lib/compiler/define.js +28 -23
  35. package/lib/compiler/extend.js +75 -18
  36. package/lib/compiler/finalize-parse-cdl.js +25 -18
  37. package/lib/compiler/index.js +27 -11
  38. package/lib/compiler/moduleLayers.js +7 -0
  39. package/lib/compiler/populate.js +26 -39
  40. package/lib/compiler/propagator.js +12 -7
  41. package/lib/compiler/resolve.js +207 -236
  42. package/lib/compiler/shared.js +100 -93
  43. package/lib/compiler/tweak-assocs.js +13 -20
  44. package/lib/compiler/utils.js +20 -6
  45. package/lib/edm/annotations/preprocessAnnotations.js +12 -13
  46. package/lib/edm/csn2edm.js +35 -37
  47. package/lib/edm/edm.js +22 -13
  48. package/lib/edm/edmAnnoPreprocessor.js +349 -0
  49. package/lib/edm/edmInboundChecks.js +85 -0
  50. package/lib/edm/edmPreprocessor.js +338 -689
  51. package/lib/edm/edmUtils.js +97 -67
  52. package/lib/gen/Dictionary.json +29 -9
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +8 -31
  55. package/lib/gen/language.tokens +105 -114
  56. package/lib/gen/languageLexer.interp +1 -34
  57. package/lib/gen/languageLexer.js +892 -1007
  58. package/lib/gen/languageLexer.tokens +95 -106
  59. package/lib/gen/languageParser.js +20629 -22474
  60. package/lib/inspect/.eslintrc.json +4 -0
  61. package/lib/inspect/index.js +14 -0
  62. package/lib/inspect/inspectModelStatistics.js +81 -0
  63. package/lib/inspect/inspectPropagation.js +189 -0
  64. package/lib/inspect/inspectUtils.js +44 -0
  65. package/lib/json/from-csn.js +74 -69
  66. package/lib/json/to-csn.js +17 -14
  67. package/lib/language/antlrParser.js +2 -2
  68. package/lib/language/docCommentParser.js +61 -38
  69. package/lib/language/errorStrategy.js +52 -40
  70. package/lib/language/genericAntlrParser.js +424 -292
  71. package/lib/language/language.g4 +604 -687
  72. package/lib/language/multiLineStringParser.js +14 -42
  73. package/lib/language/textUtils.js +44 -0
  74. package/lib/main.d.ts +28 -42
  75. package/lib/main.js +104 -81
  76. package/lib/model/api.js +1 -1
  77. package/lib/model/csnRefs.js +57 -30
  78. package/lib/model/csnUtils.js +189 -287
  79. package/lib/model/revealInternalProperties.js +32 -10
  80. package/lib/model/sortViews.js +32 -31
  81. package/lib/modelCompare/compare.js +3 -0
  82. package/lib/optionProcessor.js +91 -57
  83. package/lib/render/.eslintrc.json +1 -1
  84. package/lib/render/DuplicateChecker.js +4 -7
  85. package/lib/render/manageConstraints.js +70 -2
  86. package/lib/render/toCdl.js +387 -367
  87. package/lib/render/toHdbcds.js +20 -16
  88. package/lib/render/toRename.js +44 -22
  89. package/lib/render/toSql.js +81 -59
  90. package/lib/render/utils/common.js +16 -3
  91. package/lib/render/utils/sql.js +20 -19
  92. package/lib/sql-identifier.js +6 -0
  93. package/lib/transform/db/.eslintrc.json +3 -2
  94. package/lib/transform/db/associations.js +43 -35
  95. package/lib/transform/db/cdsPersistence.js +5 -16
  96. package/lib/transform/db/constraints.js +1 -1
  97. package/lib/transform/db/expansion.js +7 -6
  98. package/lib/transform/db/flattening.js +16 -18
  99. package/lib/transform/db/transformExists.js +7 -5
  100. package/lib/transform/db/views.js +3 -3
  101. package/lib/transform/draft/.eslintrc.json +2 -2
  102. package/lib/transform/draft/db.js +6 -6
  103. package/lib/transform/draft/odata.js +6 -7
  104. package/lib/transform/forHanaNew.js +30 -24
  105. package/lib/transform/forOdataNew.js +14 -16
  106. package/lib/transform/localized.js +35 -25
  107. package/lib/transform/odata/toFinalBaseType.js +10 -10
  108. package/lib/transform/odata/typesExposure.js +17 -8
  109. package/lib/transform/odata/utils.js +1 -38
  110. package/lib/transform/transformUtilsNew.js +63 -77
  111. package/lib/transform/translateAssocsToJoins.js +2 -2
  112. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  113. package/lib/transform/universalCsn/coreComputed.js +11 -6
  114. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  115. package/lib/utils/file.js +31 -21
  116. package/lib/utils/moduleResolve.js +0 -1
  117. package/lib/utils/timetrace.js +20 -21
  118. package/package.json +34 -4
  119. package/share/messages/syntax-expected-integer.md +9 -8
  120. package/doc/ApiMigration.md +0 -237
  121. package/doc/CommandLineMigration.md +0 -58
  122. package/doc/ErrorMessages.md +0 -175
  123. package/doc/FioriAnnotations.md +0 -94
  124. package/doc/ODataTransformation.md +0 -273
  125. package/lib/backends.js +0 -529
  126. package/lib/checks/unknownMagic.js +0 -41
  127. package/lib/fix_antlr4-8_warning.js +0 -56
package/lib/api/main.js CHANGED
@@ -2,20 +2,20 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- const prepareOptions = require('./options');
6
- const backends = require('../backends');
7
- const { setProp } = require('../base/model');
8
- const { emptyLocation } = require('../base/location');
9
- const { CompilationError, makeMessageFunction } = require('../base/messages');
10
- const { recompileX } = require('../compiler/index');
11
- const { compactModel, sortCsn } = require('../json/to-csn');
12
- const { transform4odataWithCsn } = require('../transform/forOdataNew.js');
13
- const { toSqlDdl } = require('../render/toSql');
14
- const { compareModels } = require('../modelCompare/compare');
15
- const sortViews = require('../model/sortViews');
16
- const { getResultingName } = require('../model/csnUtils');
17
- const { timetrace } = require('../utils/timetrace');
18
- const { transformForHanaWithCsn } = require('../transform/forHanaNew');
5
+ const prepareOptions = lazyload('./options');
6
+ const baseModel = lazyload('../base/model');
7
+ const location = lazyload('../base/location');
8
+ const messages = lazyload('../base/messages');
9
+ const compiler = lazyload('../compiler/index');
10
+ const toCsn = lazyload('../json/to-csn');
11
+ const forOdataNew = lazyload('../transform/forOdataNew.js');
12
+ const toSql = lazyload('../render/toSql');
13
+ const toCdl = require('../render/toCdl');
14
+ const modelCompare = lazyload('../modelCompare/compare');
15
+ const sortViews = lazyload('../model/sortViews');
16
+ const csnUtils = lazyload('../model/csnUtils');
17
+ const timetrace = lazyload('../utils/timetrace');
18
+ const forHanaNew = lazyload('../transform/forHanaNew');
19
19
 
20
20
  /**
21
21
  * Return the artifact name for use for the hdbresult object
@@ -26,17 +26,15 @@ const { transformForHanaWithCsn } = require('../transform/forHanaNew');
26
26
  * @returns {string} Name with . replaced as _ in some places
27
27
  */
28
28
  function getFileName(artifactName, csn) {
29
- return getResultingName(csn, 'quoted', artifactName);
29
+ return csnUtils.getResultingName(csn, 'quoted', artifactName);
30
30
  }
31
31
 
32
- const propertyToCheck = {
33
- odata: 'toOdata',
34
- };
35
-
36
32
  const { cloneCsnNonDict } = require('../model/csnUtils');
37
33
  const { toHdbcdsSource } = require('../render/toHdbcds');
38
34
  const { ModelError } = require('../base/error');
39
35
  const { forEach, forEachKey } = require('../utils/objectUtils');
36
+ const { checkRemovedDeprecatedFlags } = require('../base/model');
37
+ const { csn2edm, csn2edmAll } = require('../edm/csn2edm');
40
38
 
41
39
  const relevantGeneralOptions = [ /* for future generic options */ ];
42
40
  const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
@@ -54,15 +52,14 @@ const warnAboutMismatchOdata = [ 'odataVersion' ];
54
52
  function attachTransformerCharacteristics(csn, transformation, options,
55
53
  relevantOptionNames, optionalOptionNames = []) {
56
54
  const relevant = {};
57
- const propName = propertyToCheck[transformation];
58
55
  for (const name of relevantOptionNames ) {
59
- if (options[propName][name] !== undefined)
60
- relevant[name] = options[propName][name];
56
+ if (options[name] !== undefined)
57
+ relevant[name] = options[name];
61
58
  }
62
59
 
63
60
  for (const name of optionalOptionNames ) {
64
- if (options[propName][name] !== undefined)
65
- relevant[name] = options[propName][name];
61
+ if (options[name] !== undefined)
62
+ relevant[name] = options[name];
66
63
  }
67
64
 
68
65
  for (const name of relevantGeneralOptions ) {
@@ -70,10 +67,10 @@ function attachTransformerCharacteristics(csn, transformation, options,
70
67
  relevant[name] = options[name];
71
68
  }
72
69
  if (!csn.meta)
73
- setProp(csn, 'meta', {});
70
+ baseModel.setProp(csn, 'meta', {});
74
71
 
75
- setProp(csn.meta, 'options', relevant);
76
- setProp(csn.meta, 'transformation', transformation);
72
+ baseModel.setProp(csn.meta, 'options', relevant);
73
+ baseModel.setProp(csn.meta, 'transformation', transformation);
77
74
  }
78
75
 
79
76
  /**
@@ -92,7 +89,7 @@ function checkPreTransformedCsn(csn, options, relevantOptionNames, warnAboutMism
92
89
  // Not able to check
93
90
  return;
94
91
  }
95
- const { error, warning, throwWithAnyError } = makeMessageFunction(csn, options, module);
92
+ const { error, warning, throwWithAnyError } = messages.makeMessageFunction(csn, options, module);
96
93
 
97
94
  for (const name of relevantOptionNames ) {
98
95
  if (options[name] !== csn.meta.options[name])
@@ -128,7 +125,7 @@ function isPreTransformed(csn, transformation) {
128
125
  * @returns {object} Return an oData-pre-processed CSN
129
126
  */
130
127
  function odataInternal(csn, internalOptions) {
131
- const oDataCsn = transform4odataWithCsn(csn, internalOptions);
128
+ const oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions);
132
129
  attachTransformerCharacteristics(oDataCsn, 'odata', internalOptions, relevantOdataOptions, warnAboutMismatchOdata);
133
130
  return oDataCsn;
134
131
  }
@@ -150,13 +147,13 @@ function odata(csn, options = {}) {
150
147
  *
151
148
  * @param {object} csn CSN to process
152
149
  * @param {object} [externalOptions={}] Options
153
- * @returns {CDL} { <artifactName>: <CDL representation>, ...}
150
+ * @returns {object} { model: string, namespace: string, unappliedExtensions: string }
154
151
  */
155
152
  function cdl(csn, externalOptions = {}) {
156
153
  const internalOptions = prepareOptions.to.cdl(externalOptions);
157
- const { result } = backends.toCdlWithCsn(cloneCsnNonDict(csn, internalOptions), internalOptions);
158
- return result;
154
+ return toCdl.csnToCdl(cloneCsnNonDict(csn, internalOptions), internalOptions);
159
155
  }
156
+
160
157
  /**
161
158
  * Transform a CSN like to.sql
162
159
  *
@@ -168,8 +165,9 @@ function cdl(csn, externalOptions = {}) {
168
165
  function forSql(csn, options = {}) {
169
166
  const internalOptions = prepareOptions.to.sql(options);
170
167
  internalOptions.transformation = 'sql';
171
- internalOptions.toSql.csn = true;
172
- return backends.toSqlWithCsn(csn, internalOptions).csn;
168
+ const transformedCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.sql');
169
+
170
+ return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
173
171
  }
174
172
  /**
175
173
  * Transform a CSN like to.hdi
@@ -181,8 +179,10 @@ function forSql(csn, options = {}) {
181
179
  */
182
180
  function forHdi(csn, options = {}) {
183
181
  const internalOptions = prepareOptions.to.hdi(options);
184
- internalOptions.toSql.csn = true;
185
- return backends.toSqlWithCsn(csn, internalOptions).csn;
182
+ internalOptions.transformation = 'sql';
183
+ const transformedCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.hdi');
184
+
185
+ return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
186
186
  }
187
187
  /**
188
188
  * Transform a CSN like to.hdbcds
@@ -196,9 +196,9 @@ function forHdbcds(csn, options = {}) {
196
196
  const internalOptions = prepareOptions.to.hdbcds(options);
197
197
  internalOptions.transformation = 'hdbcds';
198
198
 
199
- const hanaCsn = transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
199
+ const hanaCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
200
200
 
201
- return internalOptions.testMode ? sortCsn(hanaCsn, internalOptions) : hanaCsn;
201
+ return internalOptions.testMode ? toCsn.sortCsn(hanaCsn, internalOptions) : hanaCsn;
202
202
  }
203
203
 
204
204
  /**
@@ -212,16 +212,64 @@ function sql(csn, options = {}) {
212
212
  const internalOptions = prepareOptions.to.sql(options);
213
213
  internalOptions.transformation = 'sql';
214
214
 
215
- // we need the CSN for view sorting
216
- internalOptions.toSql.csn = true;
215
+ const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
217
216
 
218
- const intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
217
+ if (internalOptions.sqlDialect === 'postgres' && !baseModel.isBetaEnabled(internalOptions, 'postgres'))
218
+ error(null, null, 'sqlDialect: \'postgres\' is only supported with beta-flag \'postgres\'');
219
219
 
220
- const result = sortViews(intermediateResult);
220
+ // we need the CSN for view sorting
221
+ const transformedCsn = forSql(csn, options);
222
+ const sqls = toSql.toSqlDdl(transformedCsn, internalOptions);
223
+
224
+ const result = sortViews({ csn: transformedCsn, sql: sqls.sql });
221
225
 
222
226
  return result.map(obj => obj.sql).filter(create => create);
223
227
  }
224
228
 
229
+ /**
230
+ * Render the given deltaCSN as SQL.
231
+ *
232
+ * @param {CSN.Model} csn A clean input CSN
233
+ * @param {CSN.Model} deltaCsn A CSN representing new entities and extensions
234
+ * @param {SqlOptions} [options={}] Options
235
+ * @returns {object} - definitions: An array of objects with all artifacts in the after-image. Each object specifies
236
+ * the artifact filename, the suffix, and the corresponding SQL statement to create
237
+ * the artifact.
238
+ * - deletions: An array of objects with the deleted artifacts. Each object specifies the artifact
239
+ * filename and the suffix.
240
+ * - migrations: An array of objects with the changed (migrated) artifacts. Each object specifies the
241
+ * artifact filename, the suffix, and the changeset (an array of changes, each specifying
242
+ * whether it incurs potential data loss, and its respective SQL statement(s), with
243
+ * multiple statements concatenated as a multi-line string in case the change e.g.
244
+ * consists of a column drop and add).
245
+ */
246
+ function mtx(csn, deltaCsn, options = {}) {
247
+ if (!baseModel.isBetaEnabled(options, 'to.mtx'))
248
+ throw new Error('to.mtx is only available with beta flag `to.mtx`');
249
+
250
+ const internalOptions = prepareOptions.to.sql(options);
251
+ // TODO: Use compiler.compileSources() when this function is moved to lib/main.js
252
+ const merged = toCsn.compactModel(compiler.compileSourcesX({ 'base.csn': csn, 'delta.csn': deltaCsn }), options);
253
+ const baseSql = forSql(csn, options);
254
+ const mergedSql = forSql(merged, options);
255
+ const diff = modelCompare.compareModels(baseSql, mergedSql, options);
256
+ // Delete artifacts that are already present in csn
257
+ Object.keys(baseSql.definitions).forEach((artifactName) => {
258
+ if (diff.definitions[artifactName]) // don't render again, but need info for primary key extension
259
+ diff.definitions[artifactName]['@cds.persistence.skip'] = true;
260
+ });
261
+
262
+ internalOptions.forHana = true;
263
+ internalOptions.beta.sqlExtensions = true;
264
+ const { deletions, migrations, ...additions } = toSql.toSqlDdl(diff, internalOptions);
265
+
266
+ return {
267
+ additions: createSqlDefinitions(additions, mergedSql),
268
+ deletions: createSqlDeletions(deletions, baseSql),
269
+ migrations: createSqlMigrations(migrations, mergedSql),
270
+ };
271
+ }
272
+
225
273
  /**
226
274
  * Process the given CSN into HDI artifacts.
227
275
  *
@@ -233,12 +281,8 @@ function hdi(csn, options = {}) {
233
281
  const internalOptions = prepareOptions.to.hdi(options);
234
282
 
235
283
  // we need the CSN for view sorting
236
- internalOptions.toSql.csn = true;
237
-
238
- const intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
239
-
240
- const sqlCSN = intermediateResult.csn;
241
- delete intermediateResult.csn;
284
+ const sqlCSN = forHdi(csn, options);
285
+ const sqls = toSql.toSqlDdl(sqlCSN, internalOptions);
242
286
 
243
287
  if (internalOptions.testMode) {
244
288
  // All this mapping is needed because sortViews crossmatches
@@ -246,7 +290,7 @@ function hdi(csn, options = {}) {
246
290
  // But we also need to return it with the correct file ending in the end
247
291
  // so remember and do lot's of mapping here.
248
292
 
249
- const flat = flattenResultStructure(intermediateResult);
293
+ const flat = flattenResultStructure(sqls);
250
294
 
251
295
  const nameMapping = Object.create(null);
252
296
  const sqlArtifactsWithCSNNamesToSort = Object.create(null);
@@ -277,7 +321,7 @@ function hdi(csn, options = {}) {
277
321
  return sorted;
278
322
  }
279
323
 
280
- return remapNames(flattenResultStructure(intermediateResult), sqlCSN, k => !k.endsWith('.hdbindex'));
324
+ return remapNames(flattenResultStructure(sqls), sqlCSN, k => !k.endsWith('.hdbindex'));
281
325
  }
282
326
  /**
283
327
  * Remap names so that they stay consistent between v1 and v2
@@ -343,91 +387,69 @@ function remapName(key, csn, filter = () => true) {
343
387
  * consists of a column drop and add).
344
388
  */
345
389
  function hdiMigration(csn, options, beforeImage) {
346
- /**
347
- * Swap arguments in case of inverted argument order.
348
- * This is for backward compatibility with @sap/cds@4.5.(2…3).
349
- *
350
- * @todo Remove in cds-compiler@2.x
351
- * @param {HdiOptions|CSN.Model} inputOptions Options or CSN image
352
- * @param {HdiOptions|CSN.Model} inputBeforeImage CSN image or options
353
- * @returns {Array} Array where the real options come first
354
- */
355
- function backwardCompatible(inputOptions, inputBeforeImage) {
356
- /**
357
- * Check whether the given argument is a CSN
358
- *
359
- * @param {object} arg Argument to verify
360
- * @returns {boolean} True if it is a CSN
361
- */
362
- function isBeforeImage(arg) {
363
- return arg === null || [ 'definitions', 'meta', '$version' ].some(key => key in arg);
364
- }
365
- return isBeforeImage(inputBeforeImage)
366
- ? [ inputOptions, inputBeforeImage ]
367
- : [ inputBeforeImage, inputOptions ];
368
- }
369
- [ options, beforeImage ] = backwardCompatible(options, beforeImage);
370
-
371
390
  const internalOptions = prepareOptions.to.hdi(options);
372
- internalOptions.toSql.csn = true;
373
391
 
374
392
  // Prepare after-image.
375
- // FIXME: Is this needed?
376
- // cloneCsnMessages(csn, options, internalOptions);
377
- const afterImage = backends.toSqlWithCsn(csn, internalOptions).csn;
393
+ const afterImage = forHdi(csn, options);
378
394
 
379
395
  // Compare both images.
380
- const diff = compareModels(beforeImage || afterImage, afterImage, internalOptions);
396
+ const diff = modelCompare.compareModels(beforeImage || afterImage, afterImage, internalOptions);
381
397
 
382
398
  // Convert the diff to SQL.
383
399
  internalOptions.forHana = true; // Make it pass the SQL rendering
384
- const { deletions, migrations, ...hdbkinds } = toSqlDdl(diff, internalOptions);
400
+ const { deletions, migrations, ...hdbkinds } = toSql.toSqlDdl(diff, internalOptions);
385
401
 
386
402
  return {
387
403
  afterImage,
388
- definitions: createDefinitions(),
389
- deletions: createDeletions(),
390
- migrations: createMigrations(),
404
+ definitions: createSqlDefinitions(hdbkinds, afterImage),
405
+ deletions: createSqlDeletions(deletions, beforeImage),
406
+ migrations: createSqlMigrations(migrations, afterImage),
391
407
  };
408
+ }
392
409
 
393
- /**
394
- * From the given HDI artifacts, create the the correct result structure.
395
- *
396
- * @returns {object[]} Array of objects, each having: name, suffix and sql
397
- */
398
- function createDefinitions() {
399
- const result = [];
400
- forEach(hdbkinds, (kind, artifacts) => {
401
- const suffix = `.${ kind }`;
402
- forEach(artifacts, (name, sqlStatement) => {
403
- if ( kind !== 'hdbindex' )
404
- result.push({ name: getFileName(name, afterImage), suffix, sql: sqlStatement });
405
- else
406
- result.push({ name, suffix, sql: sqlStatement });
407
- });
410
+ /**
411
+ * From the given SQLs, create the correct result structure.
412
+ *
413
+ * @param {object} hdbkinds Object of hdbkinds (such as `hdbindex`) mapped to dictionary of artifacts.
414
+ * @param {CSN.Model} afterImage CSN, used to create correct file names in result structure.
415
+ * @returns {object[]} Array of objects, each having: name, suffix and sql
416
+ */
417
+ function createSqlDefinitions(hdbkinds, afterImage) {
418
+ const result = [];
419
+ forEach(hdbkinds, (kind, artifacts) => {
420
+ const suffix = `.${ kind }`;
421
+ forEach(artifacts, (name, sqlStatement) => {
422
+ if ( kind !== 'hdbindex' )
423
+ result.push({ name: getFileName(name, afterImage), suffix, sql: sqlStatement });
424
+ else
425
+ result.push({ name, suffix, sql: sqlStatement });
408
426
  });
409
- return result;
410
- }
411
- /**
412
- * From the given deletions, create the correct result structure.
413
- *
414
- * @returns {object[]} Array of objects, each having: name and suffix - only .hdbtable as suffix for now
415
- */
416
- function createDeletions() {
417
- const result = [];
418
- forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
419
- return result;
420
- }
421
- /**
422
- * From the given migrations, create the correct result structure.
423
- *
424
- * @returns {object[]} Array of objects, each having: name, suffix and changeset.
425
- */
426
- function createMigrations() {
427
- const result = [];
428
- forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
429
- return result;
430
- }
427
+ });
428
+ return result;
429
+ }
430
+ /**
431
+ * From the given deletions, create the correct result structure.
432
+ *
433
+ * @param {object} deletions Dictionary of deletions, only keys are used.
434
+ * @param {CSN.Model} beforeImage CSN used to create correct file names in result structure.
435
+ * @returns {object[]} Array of objects, each having: name and suffix - only .hdbtable as suffix for now
436
+ */
437
+ function createSqlDeletions(deletions, beforeImage) {
438
+ const result = [];
439
+ forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
440
+ return result;
441
+ }
442
+ /**
443
+ * From the given migrations, create the correct result structure.
444
+ *
445
+ * @param {object} migrations Dictionary of changesets (migrations).
446
+ * @param {CSN.Model} afterImage CSN used to create correct file names in result structure.
447
+ * @returns {object[]} Array of objects, each having: name, suffix and changeset.
448
+ */
449
+ function createSqlMigrations(migrations, afterImage) {
450
+ const result = [];
451
+ forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
452
+ return result;
431
453
  }
432
454
 
433
455
  hdi.migration = hdiMigration;
@@ -440,14 +462,14 @@ hdi.migration = hdiMigration;
440
462
  * @returns {HDBCDS} { <filename>:<content>, ...}
441
463
  */
442
464
  function hdbcds(csn, options = {}) {
443
- timetrace.start('to.hdbcds');
465
+ timetrace.timetrace.start('to.hdbcds');
444
466
  const internalOptions = prepareOptions.to.hdbcds(options);
445
467
  internalOptions.transformation = 'hdbcds';
446
468
 
447
469
  const hanaCsn = forHdbcds(csn, internalOptions);
448
470
 
449
471
  const result = flattenResultStructure(toHdbcdsSource(hanaCsn, internalOptions));
450
- timetrace.stop();
472
+ timetrace.timetrace.stop();
451
473
  return result;
452
474
  }
453
475
  /**
@@ -469,11 +491,11 @@ function edm(csn, options = {}) {
469
491
  let servicesEdmj;
470
492
  if (isPreTransformed(csn, 'odata')) {
471
493
  checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
472
- servicesEdmj = backends.preparedCsnToEdm(csn, service, internalOptions);
494
+ servicesEdmj = preparedCsnToEdm(csn, service, internalOptions);
473
495
  }
474
496
  else {
475
497
  const oDataCsn = odataInternal(csn, internalOptions);
476
- servicesEdmj = backends.preparedCsnToEdm(oDataCsn, service, internalOptions);
498
+ servicesEdmj = preparedCsnToEdm(oDataCsn, service, internalOptions);
477
499
  }
478
500
  return servicesEdmj.edmj;
479
501
  }
@@ -489,9 +511,9 @@ edm.all = edmall;
489
511
  */
490
512
  function edmall(csn, options = {}) {
491
513
  const internalOptions = prepareOptions.to.edm(options);
492
- const { error } = makeMessageFunction(csn, internalOptions, 'for.odata');
514
+ const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
493
515
 
494
- if (internalOptions.version === 'v2')
516
+ if (internalOptions.odataVersion === 'v2')
495
517
  error(null, null, 'OData JSON output is not available for OData V2');
496
518
 
497
519
  const result = {};
@@ -503,13 +525,11 @@ function edmall(csn, options = {}) {
503
525
  else
504
526
  oDataCsn = odataInternal(csn, internalOptions);
505
527
 
506
- const servicesJson = backends.preparedCsnToEdmAll(oDataCsn, internalOptions);
528
+ const servicesJson = preparedCsnToEdmAll(oDataCsn, internalOptions);
507
529
  const services = servicesJson.edmj;
508
- for (const serviceName in services) {
509
- const lEdm = services[serviceName];
510
- // FIXME: Why only metadata_json - isn't this rather a 'combined_json' ? If so, rename it!
511
- result[serviceName] = lEdm;
512
- }
530
+ for (const serviceName in services)
531
+ result[serviceName] = services[serviceName];
532
+
513
533
  return result;
514
534
  }
515
535
  /**
@@ -531,11 +551,11 @@ function edmx(csn, options = {}) {
531
551
  let services;
532
552
  if (isPreTransformed(csn, 'odata')) {
533
553
  checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
534
- services = backends.preparedCsnToEdmx(csn, service, internalOptions);
554
+ services = preparedCsnToEdmx(csn, service, internalOptions);
535
555
  }
536
556
  else {
537
557
  const oDataCsn = odataInternal(csn, internalOptions);
538
- services = backends.preparedCsnToEdmx(oDataCsn, service, internalOptions);
558
+ services = preparedCsnToEdmx(oDataCsn, service, internalOptions);
539
559
  }
540
560
 
541
561
  return services.edmx;
@@ -562,7 +582,7 @@ function edmxall(csn, options = {}) {
562
582
  else
563
583
  oDataCsn = odataInternal(csn, internalOptions);
564
584
 
565
- const servicesEdmx = backends.preparedCsnToEdmxAll(oDataCsn, internalOptions);
585
+ const servicesEdmx = preparedCsnToEdmxAll(oDataCsn, internalOptions);
566
586
  const services = servicesEdmx.edmx;
567
587
  // Create annotations and metadata once per service
568
588
  for (const serviceName in services) {
@@ -573,6 +593,79 @@ function edmxall(csn, options = {}) {
573
593
  return result;
574
594
  }
575
595
 
596
+ /**
597
+ * Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
598
+ * using 'options'
599
+ *
600
+ * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
601
+ * @param {string} service Service name to use. If you want all services, use preparedCsnToEdmxAll()
602
+ * @param {ODataOptions} options OData / EDMX specific options.
603
+ * @returns {object} Rendered EDMX string for the given service.
604
+ */
605
+ function preparedCsnToEdmx(csn, service, options) {
606
+ const e = csn2edm(csn, service, options);
607
+ return {
608
+ edmx: (e ? e.toXML('all') : undefined),
609
+ };
610
+ }
611
+
612
+ /**
613
+ * Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
614
+ * using 'options'.
615
+ *
616
+ * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
617
+ * @param {ODataOptions} options OData / EDMX specific options.
618
+ * @returns {object} Dictionary of rendered EDMX strings for each service.
619
+ */
620
+ function preparedCsnToEdmxAll(csn, options) {
621
+ const edmxResult = csn2edmAll(csn, options);
622
+ for (const service in edmxResult)
623
+ edmxResult[service] = edmxResult[service].toXML('all');
624
+
625
+ return {
626
+ edmx: edmxResult,
627
+ };
628
+ }
629
+
630
+ /**
631
+ * Generate edm-json for given 'service' based on 'csn' (new-style compact, already prepared for OData)
632
+ * using 'options'
633
+ *
634
+ * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
635
+ * @param {string} service Service name for which EDMX should be rendered.
636
+ * @param {ODataOptions} options OData / EDMX specific options.
637
+ * @returns {object} Rendered EDM JSON object for of the given service.
638
+ */
639
+ function preparedCsnToEdm(csn, service, options) {
640
+ // Override OData version as edm json is always v4
641
+ options.odataVersion = 'v4';
642
+ const e = csn2edm(csn, service, options);
643
+ return {
644
+ edmj: (e ? e.toJSON() : undefined),
645
+ };
646
+ }
647
+
648
+ /**
649
+ * Generate edm-json for given 'service' based on 'csn' (new-style compact, already prepared for OData)
650
+ * using 'options'
651
+ *
652
+ * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
653
+ * @param {ODataOptions} options OData / EDMX specific options.
654
+ * @returns {object} Dictionary of rendered EDM JSON objects for each service.
655
+ */
656
+ function preparedCsnToEdmAll(csn, options) {
657
+ // Override OData version as edm json is always v4
658
+ options.odataVersion = 'v4';
659
+ const edmj = csn2edmAll(csn, options);
660
+ for (const service in edmj)
661
+ edmj[service] = edmj[service].toJSON();
662
+
663
+ return {
664
+ edmj,
665
+ };
666
+ }
667
+
668
+
576
669
  /**
577
670
  * Flatten the result structure to a flat map.
578
671
  *
@@ -606,7 +699,11 @@ module.exports = {
606
699
  for_sql: publishCsnProcessor(forSql, 'for.sql'),
607
700
  for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
608
701
  for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
609
- /** */
702
+ /** beta - WIP */
703
+ mtx: publishCsnProcessor(mtx, 'to.mtx'),
704
+ /** Deprecated, will be removed in cds-compiler@v4 */
705
+ preparedCsnToEdmx,
706
+ preparedCsnToEdm,
610
707
  };
611
708
 
612
709
 
@@ -639,25 +736,29 @@ function publishCsnProcessor( processor, _name ) {
639
736
  */
640
737
  function api( csn, options = {}, ...args ) {
641
738
  try {
739
+ if (options.deprecated) {
740
+ const messageFunctions = messages.makeMessageFunction(csn, options, 'api');
741
+ checkRemovedDeprecatedFlags( options, messageFunctions );
742
+ }
642
743
  checkOutdatedOptions( options );
643
744
  return processor( csn, options, ...args );
644
745
  }
645
746
  catch (err) {
646
- if (err instanceof CompilationError || options.noRecompile || isPreTransformed(csn, 'odata')) // we cannot recompile a pre-transformed CSN
747
+ if (err instanceof messages.CompilationError || options.noRecompile || isPreTransformed(csn, 'odata')) // we cannot recompile a pre-transformed CSN
647
748
  throw err;
648
749
 
649
750
  if (options.testMode && !(err instanceof TypeError) &&
650
751
  !(err instanceof ModelError))
651
752
  throw err;
652
753
 
653
- const { info } = makeMessageFunction( csn, options, 'compile' );
654
- const msg = info( 'api-recompiled-csn', emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
754
+ const { info } = messages.makeMessageFunction( csn, options, 'compile' );
755
+ const msg = info( 'api-recompiled-csn', location.emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
655
756
  if (options.internalMsg)
656
757
  msg.error = err; // Attach original error
657
758
 
658
759
  // next line to be replaced by CSN parser call which reads the CSN object
659
- const xsn = recompileX(csn, options);
660
- const recompiledCsn = compactModel(xsn);
760
+ const xsn = compiler.recompileX(csn, options);
761
+ const recompiledCsn = toCsn.compactModel(xsn);
661
762
  return processor( recompiledCsn, options, ...args );
662
763
  }
663
764
  }
@@ -674,34 +775,59 @@ const oldBackendOptionNames = [ 'toSql', 'toOdata', 'toHana', 'forHana' ];
674
775
  * @param {CSN.Options} options Backend options
675
776
  */
676
777
  function checkOutdatedOptions(options) {
677
- if (!options)
678
- return;
679
- const { warning } = makeMessageFunction(null, options, 'api');
778
+ const { error, throwWithError } = messages.makeMessageFunction(null, options, 'api');
680
779
 
681
- // This warning has been emitted once, we don't need to emit it again.
682
- if (options.messages && options.messages.some(m => m.messageId === 'api-invalid-option'))
780
+ // This error has been emitted once, we don't need to emit it again.
781
+ if (options.messages?.some(m => m.messageId === 'api-invalid-option')) {
782
+ throwWithError();
683
783
  return;
784
+ }
684
785
 
685
786
  for (const name of oldBackendOptionNames) {
686
787
  if (typeof options[name] === 'object') // may be a boolean due to internal options
687
- warning('api-invalid-option', null, { '#': 'std', name });
788
+ error('api-invalid-option', null, { '#': 'std', name });
688
789
  }
689
790
 
690
791
  if (options.magicVars)
691
- warning('api-invalid-option', null, { '#': 'magicVars' });
792
+ error('api-invalid-option', null, { '#': 'magicVars' });
692
793
 
693
794
  // Don't check `options.magicVars`. It's likely that the user renamed `magicVars` but
694
795
  // forgot about user -> $user and locale -> $user.locale
695
- if (options.variableReplacements) {
696
- if (options.variableReplacements.user)
697
- warning('api-invalid-option', null, { '#': 'user' });
698
- if (options.variableReplacements.locale)
699
- warning('api-invalid-option', null, { '#': 'locale' });
700
- }
796
+ if (options.variableReplacements?.user)
797
+ error('api-invalid-option', null, { '#': 'user' });
798
+ if (options.variableReplacements?.locale)
799
+ error('api-invalid-option', null, { '#': 'locale' });
701
800
 
702
801
  forEachKey(options.variableReplacements || {}, (name) => {
703
802
  if (!name.startsWith('$') && name !== 'user' && name !== 'locale')
704
- warning('api-invalid-option', null, { '#': 'noDollar', name });
803
+ error('api-invalid-option', null, { '#': 'noDollar', name });
804
+ });
805
+
806
+ throwWithError();
807
+ }
808
+
809
+ /**
810
+ * Load the module on-demand and not immediately.
811
+ *
812
+ * @param {string} moduleName Name of the module to load - like with require
813
+ * @returns {object} A Proxy that handles the on-demand loading
814
+ */
815
+ function lazyload(moduleName) {
816
+ let module;
817
+ return new Proxy(((...args) => {
818
+ if (!module) // eslint-disable-next-line global-require
819
+ module = require(moduleName);
820
+
821
+ if (module.apply && typeof module.apply === 'function')
822
+ return module.apply(this, args);
823
+ return module; // for destructured calls
824
+ }), {
825
+ get(target, name) {
826
+ if (!module) // eslint-disable-next-line global-require
827
+ module = require(moduleName);
828
+
829
+ return module[name];
830
+ },
705
831
  });
706
832
  }
707
833